sexta-feira, 4 de setembro de 2020

Classe TutoProjectCollectable: Funções

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.


Sumário C++