Tworzenie Interfaces w C++

en artykuł nie dotyczy interfejsu użytkownika (UI). W programowaniu istnieje koncepcja zwana interfejsem , która jest typem zawierającym funkcje, które muszą być implementowane przez klasy dziedziczące po interfejsie. Działa jako standardowy protokół komunikacyjny między różnymi typami klas.

Aby lepiej zrozumieć, stwórzmy prosty interfejs, którego można używać w kodzie C ++.

Wyszukiwarce bibliotek przejdź do folderu zawierającego klasy C ++. Kliknij prawym przyciskiem myszy wolne miejsce i wybierz opcję New C++ Class , jak pokazano na poniższym obrazku.

Na następnym ekranie musisz wybrać Unreal Interface jako klasę nadrzędną i kliknąć przycisk Next.

W polu Name wpisz Interactable. W polu Path zachowaj domyślny folder projektu. Kliknij przycisk Create Class.

Przyjrzyjmy się kodowi C ++ wygenerowanemu przez silnik Unreal Engine dla interfejsu interaktywnego. Dodałem tylko jedną linię z deklaracją funkcji Interact () . Oto zawartość pliku Interactable.h:

#pragma once

#include "CoreMinimal.h"
#include "UObject/Interface.h"
#include "Interactable.generated.h"

// This class does not need to be modified.
UINTERFACE(MinimalAPI)
class UInteractable : public UInterface
{
  GENERATED_BODY()
};


class TUTOFIRSTPERSON_API IInteractable
{
  GENERATED_BODY()

public:

  virtual void Interact(AActor* OtherActor) = 0;
};

Zauważ, że zostały zdefiniowane dwie klasy, UInteractable i IInteractable . W UInteractable klasa dziedziczy z UInterface i wykorzystuje UINTERFACE () makro. Ta klasa nie wymaga modyfikacji i istnieje tylko po to, aby interfejs był widoczny dla systemu Reflection silnika Unreal Engine.

IInteractable klasa to taka, która naprawdę reprezentuje interfejs i będą dziedziczone przez innych klas. W tej klasie deklarowane są funkcje interfejsu.

Wirtualnego kluczowe wykorzystane przed Interact () środki funkcyjne, że funkcja ta może być pominięte. Gdy deklaracja funkcji wirtualnej kończy się na „ = 0 ”, jest to czysta funkcja wirtualna , czyli nie ma implementacji w klasie bazowej.

Przykładowe użycie:

Rozwińmy przykład utworzony w poprzednim artykule:

Zmieńmy WallSconce klasę tak, że realizuje Interactable interfejs, który został utworzony powyżej. Aby to zrobić, klasa WallSconce musi dziedziczyć z klasy IInteractable .

Usuń funkcję PressSwitch () i dodaj deklarację funkcji Interact () . Plik WallSconce.h wygląda następująco:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Interactable.h"
#include "WallSconce.generated.h"

UCLASS()
class AWallSconce : public AActor, public IInteractable
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AWallSconce();

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	UPROPERTY(VisibleAnywhere)
	USceneComponent* RootScene;

	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* StaticMeshComponent;
	
	UPROPERTY(VisibleAnywhere)	
	class UPointLightComponent* PointLightComponent;
	
	virtual void Interact(AActor* OtherActor) override;
};

W pliku WallSconce.cpp usuń funkcję PressSwitch () i dodaj definicję funkcji Interact () :

#include "WallSconce.h"
#include "Components/PointLightComponent.h"

// Sets default values
AWallSconce::AWallSconce()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

  RootScene = CreateDefaultSubobject<USceneComponent>(TEXT("RootScene"));
  RootComponent = RootScene;

  StaticMeshComponent = CreateDefaultSubobject<UStaticMeshComponent>(
                                              TEXT("StaticMeshComponent"));
  StaticMeshComponent->SetupAttachment(RootScene);
  
  ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFile(
    TEXT("/Game/StarterContent/Props/SM_Lamp_Wall.SM_Lamp_Wall"));

  if (MeshFile.Succeeded())
  {
    StaticMeshComponent->SetStaticMesh(MeshFile.Object);
  }
  
  // PointLightComponent initialization
  PointLightComponent = CreateDefaultSubobject<UPointLightComponent>(
                                              TEXT("PointLightComponent"));
  PointLightComponent->SetIntensity(1000.f);
  PointLightComponent->SetLightColor(FLinearColor(1.f, 1.f, 1.f));
  PointLightComponent->SetupAttachment(RootScene);
  PointLightComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 30.0f));
}

void AWallSconce::Interact(AActor* OtherActor)
{
  PointLightComponent->ToggleVisibility();
  
  FString Message = FString::Printf(TEXT("Switch pressed by %s"), 
                                    *(OtherActor->GetName()));

  if(GEngine)
  {
    GEngine->AddOnScreenDebugMessage(-1, 10, FColor::Red, Message);
  }
}

// Called when the game starts or when spawned
void AWallSconce::BeginPlay()
{
  Super::BeginPlay();	
}

// Called every frame
void AWallSconce::Tick(float DeltaTime)
{
  Super::Tick(DeltaTime);
}

Użyjemy tego samego mapowania danych wejściowych, co w poprzednim artykule, w którym używany jest klawisz E.

Dokonamy dwóch poprawek w pliku TutoFirstPersonCharacter.cpp (ta nazwa zależy od nazwy twojego projektu). Na początku pliku zamień #include z „WallSconce.h” na „Interactable.h” :

#include "Interactable.h"

W funkcji InteractWithWorld () zamień kod wewnątrz bloku if (bHitSomething) , jak pokazano poniżej:

void ATutoFirstPersonCharacter::InteractWithWorld()
{
  float LengthOfTrace = 300.f;
	
  FVector StartLocation;
  FVector EndLocation;
  
  StartLocation = FirstPersonCameraComponent->GetComponentLocation();
  
  EndLocation = StartLocation + 
    (FirstPersonCameraComponent->GetForwardVector() * LengthOfTrace);
	
  FHitResult OutHitResult;
  FCollisionQueryParams LineTraceParams;  
   
  bool bHitSomething = GetWorld()->LineTraceSingleByChannel(OutHitResult,
                       StartLocation, EndLocation, ECC_Visibility, LineTraceParams);

  if(bHitSomething)
  {
    
    IInteractable* InteractableObject = Cast<IInteractable>(OutHitResult.GetActor());
	
    if(InteractableObject)
    {
      InteractableObject->Interact(this);
    }
  }
}

Wykonano Cast <IInteractable>, aby sprawdzić, czy aktor znaleziony przez Line Trace implementuje interfejs IInteractable . Jeśli rzutowanie powiedzie się, zmienna InteractableObject otrzymuje prawidłowe odwołanie, którego można użyć do wywołania funkcji interfejsu.

TutoFirstPersonCharacter klasa ma już odniesienia do WallSconce klasie. Odwołuje się tylko do interfejsu interaktywnego . Jeśli utworzysz inną klasę, która implementuje interfejs Interactable , nie jest konieczne modyfikowanie kodu klasy TutoFirstPersonCharacter w celu interakcji z nową klasą.

Skompiluj kod C ++. Jeśli zrobiłeś przykład z poprzedniego artykułu, masz już instancję WallSconce na ścianie. Uruchom grę i wejdź w interakcję z WallSconce za pomocą klawisza E.

Ten artykuł kończy drugą część samouczków Unreal C ++.


Źródło:https://romeroblueprints.blogspot.com/2021/01/creating-interfaces-in-c.html