A classe UActorComponent é a classe base usada na criação de componentes que podem ser adicionados em Atores.
Uma instância de UActorComponent não possui um Transform, não pode ser anexado à outro componente e não possui geometria para ser renderizada ou usada em colisão. Existem subclasses de UActorComponent que adicionam estas funcionalidades.
Como exemplo, vamos ver a hierarquia de classes da classe UStaticMeshComponent. Esta hierarquia está disponível na Unreal Engine API Reference.
- USceneComponent: Uma instância desta classe possui um Transform e pode ser anexada à outro componente.
- UPrimitiveComponent: Uma instância desta classe possui ou gera geometria que pode ser renderizada ou usada em colisão.
Se você precisar pegar a referência do Actor que contém o componente, use a função GetOwner() no componente:
AActor* ActorOwner = GetOwner();
Exemplo de uso:
Vamos criar um ActorComponent que contém variáveis para guardar o Health, Mana e Level do jogador. O componente terá uma função UpgradeLevel() que aumentará o nível do jogador e atualizará os valores máximos de Health e Mana. Este componente poderá ser usado em C++ e em Blueprints.
Crie uma classe C++ com o nome HealthManaComponent usando como classe pai a classe Actor Component como mostra a imagem abaixo.
Veja no arquivo HealthManaComponent.h que a macro UCLASS possui alguns especificadores para permitir que o componente possa ser usado em Blueprints. Adicione as variáveis e funções como mostra o código abaixo:
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "HealthManaComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class TUTOPROJECT_API UHealthManaComponent : public UActorComponent
{
GENERATED_BODY()
public:
// Sets default values for this component's properties
UHealthManaComponent();
// Called every frame
virtual void TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction) override;
UFUNCTION(BlueprintPure, Category = HealthMana)
int32 GetCurrentHealth();
UFUNCTION(BlueprintCallable, Category = HealthMana)
void SetCurrentHealth(int32 newHealth);
UFUNCTION(BlueprintPure, Category = HealthMana)
int32 GetCurrentMana();
UFUNCTION(BlueprintCallable, Category = HealthMana)
void SetCurrentMana(int32 newMana);
UFUNCTION(BlueprintPure, Category = HealthMana)
int32 GetPlayerLevel();
UFUNCTION(BlueprintCallable, Category = HealthMana)
void UpgradeLevel();
protected:
// Called when the game starts
virtual void BeginPlay() override;
int32 CurrentHealth;
int32 MaxHealth;
int32 CurrentMana;
int32 MaxMana;
int32 PlayerLevel;
};
Na implementação das funções SetCurrentHealth() e SetCurrentMana() foi usado a função FMath::Clamp() para evitar que o valor seja menor que zero ou maior que o máximo permitido.
O arquivo HealthManaComponent.cpp deve ter este conteúdo:
#include "HealthManaComponent.h"
UHealthManaComponent::UHealthManaComponent()
{
PrimaryComponentTick.bCanEverTick = true;
CurrentHealth = 100;
MaxHealth = 100;
CurrentMana = 100;
MaxMana = 100;
PlayerLevel = 1;
}
int32 UHealthManaComponent::GetCurrentHealth()
{
return CurrentHealth;
}
void UHealthManaComponent::SetCurrentHealth(int32 NewHealth)
{
CurrentHealth = FMath::Clamp<int32>(NewHealth, 0, MaxHealth);
}
int32 UHealthManaComponent::GetCurrentMana()
{
return CurrentMana;
}
void UHealthManaComponent::SetCurrentMana(int32 NewMana)
{
CurrentMana = FMath::Clamp<int32>(NewMana, 0, MaxMana);
}
int32 UHealthManaComponent::GetPlayerLevel()
{
return PlayerLevel;
}
void UHealthManaComponent::UpgradeLevel()
{
PlayerLevel++;
MaxHealth = PlayerLevel * 100;
CurrentHealth = MaxHealth;
MaxMana = PlayerLevel * 100;
CurrentMana = MaxMana;
}
// Called when the game starts
void UHealthManaComponent::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void UHealthManaComponent::TickComponent(float DeltaTime, ELevelTick TickType,
FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
}
Primeiro vamos ver como usar o HealthManaComponent em um Blueprint. Compile o código C++ e crie um Blueprint. Na aba Components do Blueprint, clique no botão Add Component. O HealthManaComponent está na categoria Custom como mostra esta imagem:
No Event Graph do Blueprint, você consegue acessar as funções do HealthManaComponent:
Agora, vamos usar o HealthManaComponent em uma classe C++. Crie uma classe C++ com o nome TestUActorComponent usando como classe pai a classe Actor. O arquivo TestUActorComponent.h deve ter este conteúdo:
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TestUActorComponent.generated.h"
UCLASS()
class TUTOPROJECT_API ATestUActorComponent : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
ATestUActorComponent();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
UPROPERTY(VisibleAnywhere)
class UHealthManaComponent* HealthManaComponent;
FTimerHandle TimerHandle;
void UpgradePlayerLevel();
};
A classe TestUActorComponent possui um Timer que chama a função UpgradePlayerLevel() a cada 5 segundos. Esta função chama a função UpgradeLevel() do HealthManaComponent e depois exibe na tela os valores atuais do componente.
Este é o conteúdo do arquivo TestUActorComponent.cpp:
#include "TestUActorComponent.h"
#include "HealthManaComponent.h"
// Sets default values
ATestUActorComponent::ATestUActorComponent()
{
// Set this actor to call Tick() every frame.
PrimaryActorTick.bCanEverTick = true;
HealthManaComponent = CreateDefaultSubobject<UHealthManaComponent>(
TEXT("HealthManaComponent"));
}
// Called when the game starts or when spawned
void ATestUActorComponent::BeginPlay()
{
Super::BeginPlay();
GetWorldTimerManager().SetTimer(TimerHandle, this,
&ATestUActorComponent::UpgradePlayerLevel, 5.f, true);
}
void ATestUActorComponent::UpgradePlayerLevel()
{
HealthManaComponent->UpgradeLevel();
FString Message = FString::Printf(TEXT("Level: %d - Health: %d - Mana: %d"),
HealthManaComponent->GetPlayerLevel(),
HealthManaComponent->GetCurrentHealth(),
HealthManaComponent->GetCurrentMana());
if(GEngine)
{
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, Message);
}
}
// Called every frame
void ATestUActorComponent::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
Compile o código C++ e adicione uma instância de TestUActorComponent no nível. Inicie o jogo e aguarde 5 segundos para ver a mensagem na tela. A imagem abaixo mostra quando o componente alcançou o nível 3: