domingo, 30 de agosto de 2020

Classe TutoProjectCollectable: Componentes

Neste artigo vamos criar a classe TutoProjectCollectable e definir os seus componentes. Esta classe é do tipo Actor e representa um item que pode ser coletado pelo jogador. 

No Content Browser, acesse a pasta TutoProject que está dentro da pasta C++ Classes. Clique com o botão direto em um espaço livre e escolha a opção New C++ Class... como mostra a imagem abaixo.


Na próxima tela escolha Actor como classe pai e clique no botão Next. 


Na tela seguinte, coloque TutoProjectCollectable no campo Name e clique no botão Create Class

Vamos usar um componente Static Mesh para representar visualmente esta classe no jogo. O Static Mesh que será utilizado é o SM_Statue com o Material M_Metal_Gold. Eles fazem parte do Starter Content. A aparência do Static Mesh vai ficar assim: 


Abra o arquivo TutoProjectCollectable.h. Observe que o código gerado pela Unreal Engine está usando o modificador de acesso public: duas vezes. Este é um código válido mas eu prefiro manter as variáveis e funções públicas em um único local. Copie a declaração da função Tick(float DeltaTime), coloque embaixo do construtor ATutoProjectCollectable() e remova o segundo bloco public:.

Vamos criar dois ponteiros para componentes, um para o Static Mesh Component e outro para um Scene Component. O  Scene Component será usado como Root para podermos definir uma posição relativa para o Static Mesh. O bloco public: do TutoProjectCollectable.h vai ficar assim:

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

...

Vamos falar sobre a macro UPROPERTY() no próximo artigo.

A configuração dos componentes é feita no arquivo TutoProjectCollectable.cpp dentro do construtor ATutoProjectCollectable(). O código abaixo mostra a criação de um USceneComponent que será referenciado pelo ponteiro RootScene. Depois é feita a atribuição do ponteiro para o RootComponent para indicar que este componente será o Root do Actor.

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

A criação do Static Mesh Component também é feita usando a função CreateDefaultSubobject(). O Static Mesh Component é anexado ao Scene Component usando a função SetupAttachment(). Depois é definido a resposta à colisão como Overlap para o Static Mesh não bloquear o movimento do jogador.

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

Se os dois componentes tivessem sido criados em um Blueprint, eles ficariam assim:


Nós criamos o Static Mesh Component, mas agora precisamos definir qual o Static Mesh que ele irá usar. É utilizado uma estrutura chamada FObjectFinder para buscar um arquivo de Static Mesh dentro do projeto. Se a busca for bem sucedida, o Static Mesh será atribuído ao componente.

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

  if (MeshFile.Succeeded())
  {
    StaticMesh->SetStaticMesh(MeshFile.Object);
  }
Para obter o caminho de um Asset no projeto, clique com o botão direito no Asset e escolha a opção Copy Reference, como mostra a imagem abaixo.


A definição do Material utilizado pelo Static Mesh Component é feita de uma forma bem parecida à definição do Static Mesh. Se o Material for encontrado, ele é definido para ser usado como o Material de índice 0 do Static Mesh.

O código do construtor no arquivo TutoProjectCollectable.cpp vai ficar assim:
// 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);
  }
}


Sumário C++