C++Porting_GameplayTag
1. Gameplay Tag?
계층 구조가 있는 Enum.
Unreal에서 추가된 시스템이다.
Project Settings > Gameplay Tags에서 설정 가능
Editor에서 .으로 계층구조를 정해 추가 가능하다
2. Blueprint 사용법
GameplayTag 자료형으로 선언
여러 Tag를 검사하고 싶은 경우 GameplayTagContainer를 사용
3. C++에서 사용법
3-1. Struct 추가
Struct 형태로 추가해서 Singleton을 이용해서 여러 부분에서 호출하는 방식으로 사용
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
struct FCombatGameplayTags
{
public:
static const FCombatGameplayTags& Get() { return GameplayTags; }
static void InitializeNativeGameplayTags();
public:
//State
FGameplayTag Character_State_Attacking;
FGameplayTag Character_State_Dead;
...
//Action
FGameplayTag Character_Action_Dodge;
FGameplayTag Character_Action_EnterCombat;
...
private:
static FCombatGameplayTags GameplayTags;
};
#include "Definitions/CombatGameplayTags.h"
#include "GameplayTagsManager.h"
FCombatGameplayTags FCombatGameplayTags::GameplayTags;
void FCombatGameplayTags::InitializeNativeGameplayTags()
{
if(GameplayTags.Character_State_Attacking.IsValid()) //Already Execute? then Do not Execute
return;
/**
* State
*/
GameplayTags.Character_State_Attacking = UGameplayTagsManager::Get().AddNativeGameplayTag(
FName("Character.State.Attacking"),
FString("State Attacking")
);
GameplayTags.Character_State_Dead = UGameplayTagsManager::Get().AddNativeGameplayTag(
FName("Character.State.Dead"),
FString("State Dead")
);
...
GameplayTags.Character_Action_Attack_SprintAttack = UGameplayTagsManager::Get().AddNativeGameplayTag(
FName("Character.Action.Attack.SprintAttack"),
FString("Action Attack SprintAttack")
);
}
AddNativeGameplayTag를 사용해서 Editor에 GameplayTag를 추가한다.
3-2. Singleton 초기화
이제 위에서 선언한 struct의 초기화 구문을 호출해줘야 하는데 보통 singleton 초기화는 AssetManager에서 이루어진다. 따라서 AssetManager를 상속한 Class를 생성
#pragma once
#include "CoreMinimal.h"
#include "Engine/AssetManager.h"
#include "CombatAssetManager.generated.h"
/**
*
*/
UCLASS()
class D1_API UCombatAssetManager : public UAssetManager
{
GENERATED_BODY()
public:
static UCombatAssetManager& Get();
protected:
virtual void StartInitialLoading() override;
};
#include "Definitions/CombatAssetManager.h"
#include "Definitions/CombatGameplayTags.h"
UCombatAssetManager& UCombatAssetManager::Get()
{
check(GEngine);
UCombatAssetManager* CombatAssetManager = Cast<UCombatAssetManager>(GEngine->AssetManager);
return *CombatAssetManager;
}
void UCombatAssetManager::StartInitialLoading()
{
Super::StartInitialLoading();
//AssetManager에서 Singleton 초기화
//Editor의 Engine General Settings에서 설정 변경 필요
FCombatGameplayTags::InitializeNativeGameplayTags();
}
Project Settings > General Settings 에서 Asset Manager Class를 설정하는 곳이 있는데 해당 부분에 만든 Class를 설정
3-3. 사용
void UStateManagerComponent::SetCurrentState(FGameplayTag NewState)
{
if (NewState != CurrentState)
{
OnStateEnd.Broadcast(CurrentState);
CurrentState = NewState;
OnStateBegin.Broadcast(CurrentState);
}
}
일반 자료형처럼 사용가능
void UStateManagerComponent::ResetState()
{
CurrentState = FGameplayTag::EmptyTag;
}
아무것도 표시하고 싶지 않다면 EmptyTag 사용
bool UStateManagerComponent::IsCurrentStateEqualToAny(FGameplayTagContainer StatesToCheck)
{
return StatesToCheck.HasTagExact(CurrentState);
}
여러 GameplayTag를 묶어서 사용하고 싶다면 FGameplayTagContainer를 사용 HasTagExact의 경우 정확하게 GameplayTag가 일치하는 것이 있으면 True 반환
//Header
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Status")
TMap<FGameplayTag, float> ActionStatCost;
//Cpp
ABaseWeapon::ABaseWeapon()
{
...
if(!FCombatGameplayTags::Get().Character_Action_Attack_LightAttack.IsValid())
FCombatGameplayTags::InitializeNativeGameplayTags();
ActionStatCost.Add(FCombatGameplayTags::Get().Character_Action_Attack_LightAttack, 10.f);
ActionStatCost.Add(FCombatGameplayTags::Get().Character_Action_Attack_HeavyAttack, 15.f);
...
ActionDamageMultiplier.Add(FCombatGameplayTags::Get().Character_Action_Attack_LightAttack, 1.f);
ActionDamageMultiplier.Add(FCombatGameplayTags::Get().Character_Action_Attack_HeavyAttack, 1.4f);
...
}
생성자에서 추가할 경우 Editor에서 해당 GameplayTag에 관한걸 조정할 수도 있다.
Editor에서는 다음과 같이 표시
void ACombatCharacter::CharacterStateBegin(FGameplayTag CharState)
{
if (FGameplayTag::EmptyTag == CharState)
{/*None*/}
else if (FCombatGameplayTags::Get().Character_State_Attacking == CharState)
{/*None*/}
else if (FCombatGameplayTags::Get().Character_State_Dodging == CharState)
{/*None*/}
else if (FCombatGameplayTags::Get().Character_State_GeneralActionState == CharState)
{/*None*/}
else if (FCombatGameplayTags::Get().Character_State_Dead == CharState)
{
PerformDeath();
}
else if (FCombatGameplayTags::Get().Character_State_Disable == CharState)
{/*None*/}
}
설정한 Singleton을 이용해서 다음과 같이 비교할 수 있다.
참고 - Signleton초기화 시점
Singleton 초기화를 할 때 해당 초기화 시점이 GameplayTag를 사용하는 Class의 생성자가 호출되는 시점보다 늦을 때가 있다 이 경우 초기화 구문을 한번만 탈 수 있게 조정할 필요가 있다.
CF-1. 예외처리를 이용
void FCombatGameplayTags::InitializeNativeGameplayTags()
{
if(GameplayTags.Character_State_Attacking.IsValid()) //Already Execute? then Do not Execute
return;
...
}
다음과 같이 초기화 문구에 return 구문을 넣어놓는다
ABaseWeapon::ABaseWeapon()
{
...
if(!FCombatGameplayTags::Get().Character_Action_Attack_LightAttack.IsValid())
FCombatGameplayTags::InitializeNativeGameplayTags();
...
}
호출하는 생성자에서는 이런 방식으로 부른다
CF-2. Editor에서 설정한 뒤 ini 파일에서 수정
Project Settings에서 하나라도 추가하게 되면
DefaultGameplayTags.ini 파일이 생성되게 된다.
따라서 Editor에서 미리 생성을 해놓아서 파일로 저장을 시켜놓던가 ini 파일을 수정하는 방식으로도
초기부터 GameplayTag를 사용할 수 있다
댓글남기기