sábado, 5 de dezembro de 2020

For loops e TActorRange

Na linguagem C++, um loop é usado para repetir um bloco de código enquanto uma condição específica for verdadeira. Um tipo de loop é o comando for.

O código abaixo mostra um exemplo de uso do comando for. A variável TotalEnemies armazena a quantidade de inimigos que devem ser criados no nível. A variável Counter é incrementada a cada execução do loop for. O bloco de código do loop for será repetido enquanto o valor de Counter for menor que o valor de TotalEnemies. Serão criados 10 instâncias da classe AEnemyActor em posições aleatórias no nível.  

int32 TotalEnemies = 10;
int32 Counter;
float XPosition;
float YPosition;
	
FVector  SpawnLocation;
FRotator SpawnRotation = FRotator(0.0f, 0.0f, 0.0f);
	
for(Counter = 0; Counter < TotalEnemies; Counter++)
{
  XPosition = FMath::RandRange(-1000, 1000);
  YPosition = FMath::RandRange(-1000, 1000);
		
  SpawnLocation = FVector(XPosition, YPosition, 130.0f);
		
  GetWorld()->SpawnActor<AEnemyActor>(AEnemyActor::StaticClass(),
                                      SpawnLocation, SpawnRotation );
}

O comando for possui uma variação chamada de ranged-based for loop. Neste tipo de for é declarada uma variável que vai receber o elemento atual de um grupo de elementos. O bloco de código será repetido até que todos os elementos tenham sido usados. O ranged-based for loop possui este formato: 

for(variable : range_expression) 
{

}

Os containers da Unreal TArray, TMap e TSet, funcionam com o ranged-based for loop.

Unreal Engine possui um template C++ chamado TActorRange que fornece uma range expression representando as instâncias de uma determinada classe que estão presentes no nível. O ranged-based for loop abaixo usa TActorRange para alternar a visibilidade de todas as instâncias da classe APointLight do nível.

for (APointLight* PointLightInstance : TActorRange<APointLight>( GetWorld() ) )
{	
  PointLightInstance->GetLightComponent()->ToggleVisibility();
}

A classe APointLight contém um LightComponent que possui a função ToggleVisibility().

As instâncias de APointLight no nível precisam ser Movable para que possam ter sua visibilidade modificada, como veremos no exemplo de uso.  


Exemplo de uso:

Para este exemplo será necessário um projeto C++ que tenha o template Third Person. Vamos adicionar vários Point Light Actors no nível e alternar a visibilidade deles pressionando a tecla Enter.

Primeiro vamos criar um mapeamento de input chamado PressSwitch que será acionado quando o jogador pressionar a tecla Enter.

No editor de nível, acesse o menu Edit->Project Settings... e na categoria Engine escolha a opção Input. Clique no símbolo + ao lado de Action Mappings, coloque o nome PressSwitch para o novo Action Mapping, e selecione a tecla Enter. O Action Mappings do projeto vai ficar assim:

Agora vamos modificar o código C++. Abra o arquivo cabeçalho da classe Character criado pelo template Third Person. O padrão de nome do arquivo é NomeProjetoCharacter.h. Por exemplo, o nome do meu projeto é TutoProject, e o nome do arquivo cabeçalho é TutoProjectCharacter.h.

No arquivo cabeçalho adicione a declaração da função ToggleLights() abaixo da função SetupPlayerInputComponent():

protected:
  // APawn interface
  virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent)
                                                                             override;
  // End of APawn interface

  void ToggleLights();
  

No arquivo cpp do Character é preciso adicionar estas linhas #include no início do arquivo:

#include "Engine/PointLight.h"
#include "Components/LightComponent.h"
#include "EngineUtils.h"
#include "Engine/World.h"

Na função SetupPlayerInputComponent() será feita a vinculação do Input PressSwitch com a função ToggleLights(), após o Input ResetVR. Mude o nome ATutoProjectCharacter pelo nome da classe Character do seu projeto e não esqueça de colocar o operador & antes do nome. Ele é usado para retornar o endereço de memória da função. 

PlayerInputComponent->BindAction("ResetVR", IE_Pressed, this,
                                 &ATutoProjectCharacter::OnResetVR);

PlayerInputComponent->BindAction("PressSwitch", IE_Pressed, this,
                                 &ATutoProjectCharacter::ToggleLights);

No final do arquivo cpp, crie a função ToggleLights() com o conteúdo abaixo. Não esqueça de mudar o nome da classe que está antes do operador "::". 

void ATutoProjectCharacter::ToggleLights()
{ 
  for (APointLight* PointLightInstance : TActorRange<APointLight>( GetWorld() ) )
  {
    PointLightInstance->GetLightComponent()->ToggleVisibility();
  }	
}

Compile o código C++. Para testar nosso exemplo, precisamos adicionar instâncias de Point Light no nível. Acesse a aba Place Actors, categoria Basic. Arraste algumas Point Light e solte em um das paredes do nível.   


Selecione cada um das instância de Point Light e mude a propriedade Mobility para Movable.


Inicie o jogo e vá para perto das instâncias de Point Light. Pressione a tecla Enter para desligar e ligar as Point Lights.



Sumário C++