segunda-feira, 11 de janeiro de 2021

Class reference and TSubclassOf

Sometimes we need to create a variable that stores a reference to a class and not to an instance of the class. For example, a class that represents a weapon can have a variable that will be used to define the class of the projectile that will be fired by the weapon.

Unreal Engine has a class template called TSubclassOf that can be used to declare variables that reference classes. This template provides type security and only allows references to the specified class or its subclasses.

The code below defines a variable using TSubclassOf that can be modified in the editor:

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

The image below shows how this variable is represented in the editor. The selection of classes is restricted to the permitted classes.


Example usage:

For this example, you need a project with the Starter Content

We are going to create two Actors, PickupSpawner and PickupActor. PickupSpawner creates an instance of PickupActor at the beginning of the game. In the level editor, we can select the PickupActor subclass that will be created by the PickupSpawner instance. 

Create a C++ class with the name PickupActor using the Actor class as the parent class. Let's keep this class simple with just a Static Mesh for visual representation. The PickupActor.h file should have this content:

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

The PickupActor class uses the Static Mesh Shape_Cone. The PickupActor.cpp file looks like this:

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

Create a C++ class with the name PickupSpawner using the Actor class as the parent class. This class contains a variable defined as TSubclassOf<APickupActor> that will allow the selection of other subclasses to be stored in the variable. This is the contents of the PickupSpawner.h file:

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

The spawn of the APickupActor instance is done in the BeginPlay() function using the class that is stored in the PickupClass variable. This is the contents of the PickupSpawner.cpp file:

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

Compile the C++ code and add three instances of PickupSpawner at the level.

We are going to create two subclasses of PickupActor using Blueprints just to change the StaticMesh.

Right click on the PickupActor class and choose the option Create Blueprint class based on PickupActor, as shown in the image below.

Put the name BP_PickupCube on the new Blueprint. On the Components tab, select the StaticMeshComponent.

In the Details tab, choose the Static Mesh Shape_Cube and compile the Blueprint.


Use these same steps to create another Blueprint with the name BP_PickupCapsule and Static Mesh Shape_NarrowCapsule.

Select one of the PickupSpawner instances that is at the level. In the Details tab, category Configuration, change the value of the PickupClass to BP_PickupCapsule. 


In another instance of PickupSpawner, select the BP_PickupCube. Start the game and see the PickupSpawner instances create different types of PickupActor 


Table of Contents C++