domingo, 8 de novembro de 2020

A classe AActor

A classe AActor é uma subclasse de UObject e representa os objetos de gameplay que podem ser adicionados ao nível. As classes que herdam de AActor tem o prefixo A.

Uma parte da funcionalidade de um AActor é obtida com o uso de componentes. As informações de translação, rotação e escala de um AActor são obtidas a partir do seu RootComponent. Então, um AActor precisa ter pelo menos um USceneComponent que seja definido como o seu RootComponent.

Você pode usar o editor de nível para adicionar Atores e eles podem ser criados em tempo de execução usando a função SpawnActor(). Existem diversas versões da função SpawnActor(), o exemplo abaixo mostra uma delas, assumindo que a classe AActorChild é uma subclasse de AActor:

FVector SpawnLocation = FVector(0.0f, 0.0f, 0.0f);
    
FRotator SpawnRotation = FRotator(0.0f, 0.0f, 0.0f);    
	
GetWorld()->SpawnActor<AActorChild>(AActorChild::StaticClass(),
                                    SpawnLocation, SpawnRotation );

Quando quiser destruir uma instância de AActor, pode usar a função Destroy() da classe AActor. Você também pode usar a função SetLifeSpan() para indicar o tempo em segundos que um AActor deve ficar vivo. Quando este tempo acabar, o AActor será destruído automaticamente.


Exemplo de uso:

Para este exemplo será necessário um projeto com o Starter Content

Vamos criar dois Atores, o CannonActor e o BulletActor. O CannonActor criará uma instância de BulletActor por segundo. O BulletActor se moverá em frente e será destruído após 15 segundos.

Crie uma classe C++ com o nome BulletActor usando como classe pai a classe Actor. O arquivo BulletActor.h deve ficar com este conteúdo: 

#pragma once

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

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

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

  virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;

  UPROPERTY(VisibleAnywhere)
  USceneComponent* RootScene;

  UPROPERTY(VisibleAnywhere)
  UStaticMeshComponent* StaticMesh;
	
  UPROPERTY(VisibleAnywhere)
  class UProjectileMovementComponent* ProjectileMovement;


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

};

A classe BulletActor possui três componentes: 

  • USceneComponent: Possui um Transform e será usado como componente Root.
  • UStaticMeshComponent: Contém o Static Mesh que representará visualmente o Ator.
  • UProjectileMovementComponent: Componente usado para movimentar o Ator. Observe que foi colocado a palavra class antes da definição da variável. Será necessário adicionar o #include desta classe no arquivo cpp.

No arquivo BulletActor.cpp, a definição do construtor fica assim:

#include "BulletActor.h"
#include "GameFramework/ProjectileMovementComponent.h"

// Sets default values
ABulletActor::ABulletActor()
{

  // 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/Shapes/Shape_Sphere.Shape_Sphere"));

  if (MeshFile.Succeeded())
  {
    StaticMesh->SetStaticMesh(MeshFile.Object);
  }
	
  ProjectileMovement = CreateDefaultSubobject<UProjectileMovementComponent>( 
                                             TEXT("ProjectileMovementComp") );
  ProjectileMovement->UpdatedComponent = StaticMesh;
  ProjectileMovement->InitialSpeed = 500.f;
  ProjectileMovement->MaxSpeed = 500.f;
  ProjectileMovement->ProjectileGravityScale = 0.f;

}

No construtor é feita a inicialização dos três componentes. O BulletActor usa o Static Mesh de uma esfera e se moverá em uma velocidade de 500 unidades por segundo sem gravidade.

As outras funções que precisam ser implementadas em BulletActor.cpp são estas:

void ABulletActor::BeginPlay()
{
  Super::BeginPlay();		
	
  SetLifeSpan(15.f);
}

void ABulletActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
  Super::NotifyActorBeginOverlap(OtherActor);

  Destroy();
}

Na função BeginPlay() é definido que a instância de ABulletActor será destruída após 15 segundos. A função NotifyActorBeginOverlap() é chamada quando a instância sobrepor outro Ator. Em nosso exemplo a instância de ABulletActor é destruída se isso acontecer.

Agora crie uma classe C++ com o nome CannonActor usando como classe pai a classe Actor. O arquivo CannonActor.h deve ficar com este conteúdo:

#pragma once

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

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

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

  UPROPERTY(VisibleAnywhere)
  USceneComponent* RootScene;

  UPROPERTY(VisibleAnywhere)
  UStaticMeshComponent* StaticMesh;
	
  FTimerHandle ShotTimerHandle;
	
  void ShootCannon();	

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

A classe ACannonActor possui um Timer que chama a função ShootCannon uma vez por segundo para criar uma instância de ABulletActor.

Este é o construtor que está no arquivo CannonActor.cpp:

#include "CannonActor.h"
#include "BulletActor.h"

// Sets default values
ACannonActor::ACannonActor()
{
  // 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);
	
  ConstructorHelpers::FObjectFinder<UStaticMesh> MeshFile(
    TEXT("/Game/StarterContent/Props/SM_MatPreviewMesh_02.SM_MatPreviewMesh_02"));

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

O Timer é configurado na função BeginPlay(). A função ShootCannon() usa a função SpawnActor() para criar uma instância de ABulletActor em uma posição e uma rotação baseados no canhão:

void ACannonActor::BeginPlay()
{
  Super::BeginPlay();
	
  GetWorldTimerManager().SetTimer(ShotTimerHandle, this,  
                                  &ACannonActor::ShootCannon, 1.f, true);	
}

void ACannonActor::ShootCannon()
{
  FRotator SpawnRotation = GetActorRotation();
	
  FVector SpawnLocation = GetActorLocation() 
                        + SpawnRotation.RotateVector(FVector(180.0f, 0.0f, 180.0f));
	
	
  GetWorld()->SpawnActor<ABulletActor>(ABulletActor::StaticClass(),
                                       SpawnLocation, SpawnRotation );
}

Compile o código C++ e adicione uma instância de CannonActor no nível. Inicie o jogo e veja o canhão disparando as balas: 


Sumário C++