Class Reference oraz TSubclassOf

Czasami musimy utworzyć zmienną, która przechowuje odniesienie do klasy, a nie do instancji tej klasy. Na przykład klasa reprezentująca broń może mieć zmienną, która zostanie użyta do zdefiniowania klasy pocisku, który zostanie wystrzelony przez tę broń.

Unreal Engine posiada szablon klasy o nazwie TSubclassOf, którego można używać do deklarowania zmiennych odwołujących się do klas. Ten szablon zapewnia bezpieczeństwo typu i zezwala tylko na odwołania do określonej klasy lub jej podklas.

Poniższy kod definiuje zmienną za pomocą TSubclassOf, którą można modyfikować w edytorze:

UPROPERTY(EditAnywhere, Category="TSubclassOf Example")
TSubclassOf<ATutoProjectCollectable> CollectableClass;

Poniższy obrazek pokazuje, jak ta zmienna jest reprezentowana w edytorze. Wybór zajęć jest ograniczony do dozwolonych klas.

Przykładowe użycie:

W tym przykładzie potrzebujesz projektu z  zawartością startową . 

Mamy zamiar stworzyć dwóch Actors, PickupSpawner i PickupActorPickupSpawner tworzy instancję PickupActor na początku gry. W edytorze poziomów możemy wybrać podklasę PickupActor, która zostanie utworzona przez instancję PickupSpawner. 

Utwórz klasę C ++ o nazwie PickupActor, używając klasy Actor jako klasy nadrzędnej. Uprośćmy tę klasę za pomocą statycznej siatki do wizualnej reprezentacji. Plik PickupActor.h powinien mieć następującą zawartość:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PickupActor.generated.h"

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

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;
};

PickupActor klasa używa Static Mesh Shape_Cone . Plik PickupActor.cpp wygląda następująco:

#include "PickupActor.h"

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

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

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

  if (MeshFile.Succeeded())
  {
    StaticMeshComponent->SetStaticMesh(MeshFile.Object);
  }
}

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

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

Utwórz klasę C ++ o nazwie PickupSpawner, używając klasy Actor jako klasy nadrzędnej. Ta klasa zawiera zmienną zdefiniowaną jako TSubclassOf , która pozwoli na wybór innych podklas, które mają być przechowywane w zmiennej. Oto zawartość pliku PickupSpawner.h:

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "PickupActor.h"
#include "PickupSpawner.generated.h"

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

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(EditAnywhere, Category=Configuration)
	TSubclassOf<APickupActor> PickupClass;
};

Spawn z APickupActor odbywa się w BeginPlay() funkcji przy użyciu klasy, który jest przechowywany w PickupClass zmiennej. Oto zawartość pliku PickupSpawner.cpp:

#include "PickupSpawner.h"

// Sets default values
APickupSpawner::APickupSpawner()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;
	
  RootScene = CreateDefaultSubobject<USceneComponent>("RootScene");
  RootComponent = RootScene;

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

  if (MeshFile.Succeeded())
  {
    StaticMeshComponent->SetStaticMesh(MeshFile.Object);
  }
}

// Called when the game starts or when spawned
void APickupSpawner::BeginPlay()
{
  Super::BeginPlay();
		
  if(PickupClass)
  {
    FVector SpawnLocation = GetActorLocation() + FVector(0.0f, 0.0f, 50.0f);
	
    FRotator SpawnRotation = FRotator(0.0f, 0.0f, 0.0f);
	
    GetWorld()->SpawnActor<APickupActor>(PickupClass, SpawnLocation, SpawnRotation );
  }
}

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

Skompiluj kod C ++ i dodaj trzy wystąpienia PickupSpawner na poziomie.

Zamierzamy stworzyć dwie podklasy PickupActor używając Blueprints tylko po to, aby zmienić StaticMesh.

Kliknij prawym przyciskiem myszy klasę PickupActor i wybierz opcję  Create Blueprint Class Based on PickupActor , jak pokazano na poniższym obrazku.

Umieść nazwę BP_PickupCube na nowym Blueprint. Na karcie Składniki wybierz StaticMeshComponent .

W zakładce Szczegóły wybierz Static Mesh Shape_Cube i skompiluj Blueprint.

Wykonaj te same kroki, aby utworzyć kolejny Blueprint o nazwie BP_PickupCapsule i Static Mesh Shape_NarrowCapsule.

Wybierz jedną z instancji PickupSpawner na tym poziomie. Na karcie Details w kategorii Konfiguracja zmień wartość PickupClass na BP_PickupCapsule . 

W innym wystąpieniu PickupSpawner wybierz BP_PickupCube. Uruchom grę i zobacz, jak instancje PickupSpawner tworzą różne typy PickupActor.  


Źródło:https://romeroblueprints.blogspot.com/2021/01/class-reference-and-tsubclassof.html