domingo, 30 de agosto de 2020

TutoProjectCollectable class: Components

In this article, we are going to create the TutoProjectCollectable class and define its components. This class is of the Actor type and represents an item that can be collected by the player.

In the Content Browser, access the TutoProject folder that is inside the C++ Classes folder. Right-click on a free space and choose the New C++ Class... option as shown in the image below.


On the next screen choose Actor as the parent class and click the Next button. 


On the last screen, write TutoProjectCollectable in the Name field and click the Create Class button.

We will use a Static Mesh component to visually represent this class in the game. The Static Mesh to be used is SM_Statue with Material M_Metal_Gold. They are part of the Starter Content. The Static Mesh will look like this: 


Open the TutoProjectCollectable.h file. Note that the code generated by Unreal Engine is using the public: access modifier twice. This is valid code but I prefer to keep public variables and functions in one place. Copy the declaration of the Tick(float DeltaTime) function, place it under the ATutoProjectCollectable() constructor, and remove the second public: block.

We are going to create two pointers for components, one for the Static Mesh Component and the other for a Scene Component. The Scene Component will be used as Root so that we can define a relative position for the Static Mesh. The public: block of TutoProjectCollectable.h will look like this:

#pragma once

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

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

	// Called every frame
	virtual void Tick(float DeltaTime) override;
    
   	UPROPERTY(VisibleAnywhere)
	USceneComponent* RootScene;

	UPROPERTY(VisibleAnywhere)
	UStaticMeshComponent* StaticMesh;

...

We'll talk about the UPROPERTY() macro in the next article.

The configuration of the components is done in the file TutoProjectCollectable.cpp inside the ATutoProjectCollectable() constructor. The code below shows the creation of a USceneComponent that will be referenced by the RootScene pointer. Then the assignment of the pointer to the RootComponent is done to indicate that this component will be the Actor Root.

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

The creation of the Static Mesh Component is also done using the CreateDefaultSubobject() function. The Static Mesh Component is attached to the Scene Component using the SetupAttachment() function. Then the collision response is defined as Overlap so that the Static Mesh does not block the player's movement.

StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMesh->SetupAttachment(RootScene);
StaticMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);

If the two components had been created in a Blueprint, they would look like this:


We created the Static Mesh Component, but now we need to define which Static Mesh it will use. A structure called FObjectFinder is used to search for a Static Mesh file within the project. If the search is successful, the Static Mesh will be assigned to the component.

ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFile(
    TEXT("/Game/StarterContent/Props/SM_Statue.SM_Statue"));

  if (MeshFile.Succeeded())
  {
    StaticMesh->SetStaticMesh(MeshFile.Object);
  }
To obtain the path of an Asset in the project, right-click the Asset, and choose the Copy Reference option, as shown in the image below.


The definition of the Material used by the Static Mesh Component is done in a very similar way to the definition of the Static Mesh. If the Material is found, it is defined to be used as the Index 0 Material of the Static Mesh.

The constructor code in the TutoProjectCollectable.cpp file will look like this:
// Sets default values
ATutoProjectCollectable::ATutoProjectCollectable()
{
  // Set this actor to call Tick() every frame.
  PrimaryActorTick.bCanEverTick = true;

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

  StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
  StaticMesh->SetupAttachment(RootScene);
  StaticMesh->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Overlap);

  ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFile(
    TEXT("/Game/StarterContent/Props/SM_Statue.SM_Statue"));

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

  ConstructorHelpers::FObjectFinder<UMaterial> MaterialFile(
    TEXT("/Game/StarterContent/Materials/M_Metal_Gold.M_Metal_Gold"));

  if (MaterialFile.Succeeded())
  {
    StaticMesh->SetMaterial(0, MaterialFile.Object);
  }
}


Table of Contents C++