Neste artigo vamos criar algumas variáveis e escrever as funções da classe TutoProjectCollectable. Terá uma função nova chamada InitStatue() e serão sobrescritas as funções da classe Actor BeginPlay() e NotifyActorBeginOverlap().
Primeiro vamos adicionar as variáveis e as declarações das funções no arquivo de cabeçalho TutoProjectCollectable.h. No bloco public: acrescente a declaração da função NotifyActorBeginOverlap() abaixo da função Tick():
...
public:
// Sets default values for this actor's properties
ATutoProjectCollectable();
// Called every frame
virtual void Tick(float DeltaTime) override;
virtual void NotifyActorBeginOverlap(AActor* OtherActor) override;
...
No bloco protected: coloque as variáveis e declarações de funções mostradas abaixo. As variáveis serão usadas nas funções que vamos escrever.
...
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
class ATutoProjectGameMode* TutoProjectGameMode;
FTimerHandle TimerHandleInitStatue;
void InitStatue();
};
Agora vamos modificar o arquivo TutoProjectCollectable.cpp. É preciso colocar #include dos arquivos TutoProjectGameMode.h e TutoProjectCharacter.h, pois vamos referenciar estas classes.
#include "TutoProjectCollectable.h"
#include "TutoProjectGameMode.h"
#include "TutoProjectCharacter.h"
Na função BeginPlay() será obtida uma referência para a instância da classe ATutoProjectGameMode que está sendo usada pelo jogo para armazenar na variável TutoProjectGameMode, da mesma forma que foi feito na classe TutoProjectHUD. Depois será chamada a função InitStatue() para inicializar a instância da classe TutoProjectCollectable.
// Called when the game starts or when spawned
void ATutoProjectCollectable::BeginPlay()
{
Super::BeginPlay();
TutoProjectGameMode = GetWorld()->GetAuthGameMode<ATutoProjectGameMode>();
InitStatue();
}
A função InitStatue() tem dois objetivos. O primeiro é definir a posição da instância dentro da área do jogo. O segundo objetivo é configurar um Timer que vai indicar o tempo que uma instância vai permanecer em uma posição. Quando este tempo acabar, o Timer vai chamar a função InitStatue() para mudar a posição da instância e configurar um novo Timer. O número de segundos para uma instância mudar de posição é o resultado da expressão 6 - Nível do Jogador.
void ATutoProjectCollectable::InitStatue()
{
float NewX = FMath::RandRange(XMinimum, XMaximum);
float NewY = FMath::RandRange(YMinimum, YMaximum);
FVector NewLocation = FVector(NewX, NewY, FloorZValue);
SetActorLocation(NewLocation);
float NewTime = 6 - TutoProjectGameMode->GetPlayerLevel();
GetWorldTimerManager().SetTimer(TimerHandleInitStatue, this,
&ATutoProjectCollectable::InitStatue, NewTime, false);
}
A função FMath::RandRange(Min, Max) retorna um número aleatório entre os valores de Min e Max. As variáveis que são definidas dentro de funções, como NewX e NewY, são descartadas assim que encerra a execução da função.
Se você tiver dúvidas sobre o funcionamento do Timer, veja o artigo onde criamos um Timer na classe TutoProjectGameMode:
Criando um Timer em Unreal C++
A última função é a NotifyActorBeginOverlap(AActor* OtherActor), que é chamada quando algum Actor estiver sobrepondo a instância. O parâmetro OtherActor é uma referência ao Actor que gerou a sobreposição. É usado o Cast para verificar se OtherActor é do tipo ATutoProjectCharacter. Se não for deste tipo, o resultado do Cast será um ponteiro nulo que é representado por nullptr.
Além disso é usado o operador not ! para verificar se o jogo não está no modo Game Over. Usamos o operador and && para verificar se as duas condições são verdadeiras. Neste caso, é chamada a função ItemCollected() da classe TutoProjectGameMode e a função Destroy() para remover a instância do jogo. Outra instância será criada usando a função SpawnActor().
void ATutoProjectCollectable::NotifyActorBeginOverlap(AActor* OtherActor)
{
Super::NotifyActorBeginOverlap(OtherActor);
if (Cast<ATutoProjectCharacter>(OtherActor) != nullptr
&& !TutoProjectGameMode->IsGameOver())
{
TutoProjectGameMode->ItemCollected();
Destroy();
FTransform DefaultTransform;
GetWorld()->SpawnActor<ATutoProjectCollectable>(this->GetClass(), DefaultTransform);
}
}
Algumas observações sobre o código acima. A função Destroy() indica que o Actor deve ser destruído, mas a destruição dele ocorre no fim da execução de um Tick. Por isso que pode ter comandos após a função Destroy().
A função SpawnActor() precisa de um FTransform para definir a posição onde será criada a instância. Foi criada a variável DefaultTransform usando os valores padrões de FTransform, porque a função BeginPlay() será executada para a nova instância e irá posicioná-la dentro da área do jogo.
O primeiro parâmetro da função SpawnActor() indica a classe da instância que será criada. Foi usado this->GetClass() para pegar a classe da instância que está executando este código. Dessa forma, se uma instância de uma classe filha de TutoProjectCollectable for coletada pelo jogador, a nova instância criada usará esta classe filha.