GameplayTags 시스템
계층적 태그로 게임 상태와 조건 관리
GameplayTags 개요
계층적 식별자 시스템
GameplayTags는 계층적 이름 구조를 가진 가벼운 식별자입니다. 문자열 비교보다 효율적이며, 부모-자식 관계를 통한 유연한 매칭을 제공합니다.
// 어빌리티 관련
Ability
Ability.Attack
Ability.Attack.Melee
Ability.Attack.Ranged
Ability.Skill
Ability.Skill.Fire
Ability.Skill.Ice
Ability.Skill.Heal
// 상태 관련
State
State.Dead
State.Stunned
State.Invulnerable
State.Casting
// 버프/디버프
Buff
Buff.AttackUp
Buff.DefenseUp
Buff.SpeedUp
Debuff
Debuff.Poison
Debuff.Slow
Debuff.Burn
// 쿨다운
Cooldown
Cooldown.Ability.Fireball
Cooldown.Ability.Heal
// 데이터 (SetByCaller용)
Data
Data.Damage
Data.Healing
Data.Duration
C++에서 태그 정의
네이티브 GameplayTag 선언
#pragma once
#include "CoreMinimal.h"
#include "GameplayTagContainer.h"
/**
* 프로젝트 전역 GameplayTag 정의
*/
namespace MyGameplayTags
{
// ========== 어빌리티 ==========
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Attack_Melee);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Attack_Ranged);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Skill_Fireball);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Skill_Heal);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Ability_Skill_Dash);
// ========== 상태 ==========
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Dead);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Stunned);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Invulnerable);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(State_Casting);
// ========== 버프/디버프 ==========
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Buff_AttackUp);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Buff_DefenseUp);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Debuff_Poison);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Debuff_Slow);
// ========== 쿨다운 ==========
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Cooldown_Ability_Fireball);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Cooldown_Ability_Heal);
// ========== 데이터 ==========
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Data_Damage);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Data_Healing);
// ========== 이벤트 ==========
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Event_Montage_AttackHit);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(Event_Combat_Death);
}
#include "MyGameplayTags.h"
namespace MyGameplayTags
{
// 어빌리티
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Attack_Melee,
"Ability.Attack.Melee", "근접 공격");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Attack_Ranged,
"Ability.Attack.Ranged", "원거리 공격");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Skill_Fireball,
"Ability.Skill.Fireball", "파이어볼 스킬");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Skill_Heal,
"Ability.Skill.Heal", "힐 스킬");
UE_DEFINE_GAMEPLAY_TAG_COMMENT(Ability_Skill_Dash,
"Ability.Skill.Dash", "대시 스킬");
// 상태
UE_DEFINE_GAMEPLAY_TAG(State_Dead, "State.Dead");
UE_DEFINE_GAMEPLAY_TAG(State_Stunned, "State.Stunned");
UE_DEFINE_GAMEPLAY_TAG(State_Invulnerable, "State.Invulnerable");
UE_DEFINE_GAMEPLAY_TAG(State_Casting, "State.Casting");
// 버프/디버프
UE_DEFINE_GAMEPLAY_TAG(Buff_AttackUp, "Buff.AttackUp");
UE_DEFINE_GAMEPLAY_TAG(Buff_DefenseUp, "Buff.DefenseUp");
UE_DEFINE_GAMEPLAY_TAG(Debuff_Poison, "Debuff.Poison");
UE_DEFINE_GAMEPLAY_TAG(Debuff_Slow, "Debuff.Slow");
// 쿨다운
UE_DEFINE_GAMEPLAY_TAG(Cooldown_Ability_Fireball, "Cooldown.Ability.Fireball");
UE_DEFINE_GAMEPLAY_TAG(Cooldown_Ability_Heal, "Cooldown.Ability.Heal");
// 데이터
UE_DEFINE_GAMEPLAY_TAG(Data_Damage, "Data.Damage");
UE_DEFINE_GAMEPLAY_TAG(Data_Healing, "Data.Healing");
// 이벤트
UE_DEFINE_GAMEPLAY_TAG(Event_Montage_AttackHit, "Event.Montage.AttackHit");
UE_DEFINE_GAMEPLAY_TAG(Event_Combat_Death, "Event.Combat.Death");
}
태그 조건 설정
어빌리티 활성화 조건으로 태그 사용
// GameplayAbility 설정 항목
// Ability Tags: 이 어빌리티를 식별하는 태그
AbilityTags:
- Ability.Skill.Fireball
// Cancel Abilities with Tag: 이 태그를 가진 다른 어빌리티 취소
CancelAbilitiesWithTag:
- Ability.Attack // 공격 중이면 취소
// Block Abilities with Tag: 이 태그를 가진 어빌리티 차단
BlockAbilitiesWithTag:
- Ability.Skill // 다른 스킬 사용 차단
// Activation Owned Tags: 활성화 중 소유자에게 부여
ActivationOwnedTags:
- State.Casting // 시전 중 상태
// Activation Required Tags: 활성화에 필요한 태그 (소유자에게 있어야 함)
ActivationRequiredTags:
- (비어있음)
// Activation Blocked Tags: 활성화 차단 태그 (소유자에게 있으면 안 됨)
ActivationBlockedTags:
- State.Dead
- State.Stunned
- Cooldown.Ability.Fireball
Ability.Attack으로 쿼리하면 Ability.Attack.Melee와 Ability.Attack.Ranged 모두 매칭됩니다. 부모 태그는 자식 태그를 포함합니다.
태그 런타임 조작
코드에서 태그 추가/제거/조회
// 태그 추가 (Loose Tags - 수동 관리)
void AddStateTag(UAbilitySystemComponent* ASC, FGameplayTag Tag)
{
if (ASC)
{
ASC->AddLooseGameplayTag(Tag);
// 또는 여러 태그
// ASC->AddLooseGameplayTags(TagContainer);
}
}
// 태그 제거
void RemoveStateTag(UAbilitySystemComponent* ASC, FGameplayTag Tag)
{
if (ASC)
{
ASC->RemoveLooseGameplayTag(Tag);
}
}
// 태그 보유 여부 확인
bool HasTag(UAbilitySystemComponent* ASC, FGameplayTag Tag)
{
if (ASC)
{
return ASC->HasMatchingGameplayTag(Tag);
}
return false;
}
// 태그 중 하나라도 보유 확인
bool HasAnyTag(UAbilitySystemComponent* ASC, FGameplayTagContainer Tags)
{
if (ASC)
{
return ASC->HasAnyMatchingGameplayTags(Tags);
}
return false;
}
// 모든 태그 보유 확인
bool HasAllTags(UAbilitySystemComponent* ASC, FGameplayTagContainer Tags)
{
if (ASC)
{
return ASC->HasAllMatchingGameplayTags(Tags);
}
return false;
}
// 태그 변경 감지
void BindToTagChange(UAbilitySystemComponent* ASC)
{
if (ASC)
{
ASC->RegisterGameplayTagEvent(
MyGameplayTags::State_Stunned,
EGameplayTagEventType::NewOrRemoved)
.AddUObject(this, &AMyActor::OnStunnedTagChanged);
}
}
void AMyActor::OnStunnedTagChanged(const FGameplayTag Tag, int32 NewCount)
{
// NewCount > 0: 태그 추가됨
// NewCount == 0: 태그 제거됨
if (NewCount > 0)
{
// 스턴 효과 적용
DisableMovement();
}
else
{
// 스턴 해제
EnableMovement();
}
}
태그 기반 로직
게임플레이 조건부 처리
// 데미지 타입에 따른 처리
void ProcessDamage(const FGameplayTagContainer& DamageTags, float Damage)
{
// 화염 데미지
if (DamageTags.HasTag(FGameplayTag::RequestGameplayTag("DamageType.Fire")))
{
// 화상 디버프 적용
ApplyBurnDebuff();
// 얼음 타입 약점
if (HasTag(ASC, FGameplayTag::RequestGameplayTag("Attribute.Weakness.Fire")))
{
Damage *= 1.5f;
}
}
// 얼음 데미지
if (DamageTags.HasTag(FGameplayTag::RequestGameplayTag("DamageType.Ice")))
{
// 슬로우 디버프 적용
ApplySlowDebuff();
}
// 물리 데미지
if (DamageTags.HasTag(FGameplayTag::RequestGameplayTag("DamageType.Physical")))
{
// 방어력 감소 적용
Damage = Damage * (100.0f / (100.0f + Defense));
}
// 최종 데미지 적용
ApplyFinalDamage(Damage);
}
// AI 행동 결정
EAIBehavior DecideAIBehavior(UAbilitySystemComponent* ASC)
{
// 죽은 상태면 아무것도 안 함
if (HasTag(ASC, MyGameplayTags::State_Dead))
{
return EAIBehavior::None;
}
// 스턴 상태면 대기
if (HasTag(ASC, MyGameplayTags::State_Stunned))
{
return EAIBehavior::Idle;
}
// 체력 낮으면 도주
if (GetHealthPercent() < 0.2f)
{
return EAIBehavior::Flee;
}
// 기본: 공격
return EAIBehavior::Attack;
}
핵심 요약
- GameplayTags는 계층적 식별자로 상태와 조건 관리에 최적
- UE_DECLARE/DEFINE_GAMEPLAY_TAG로 C++에서 태그 정의
- ActivationBlockedTags로 어빌리티 활성화 조건 설정
- AddLooseGameplayTag로 런타임 태그 조작
- RegisterGameplayTagEvent로 태그 변경 감지
도전 과제
배운 내용을 직접 실습해보세요
DefaultGameplayTags.ini에 State.Dead, State.Stunned, Ability.Skill.Fire, Ability.Skill.Ice, Buff.AttackUp, Debuff.Poison 등의 태그 계층을 정의하세요. C++에서 FGameplayTag::RequestGameplayTag()로 태그를 얻으세요.
FGameplayTagContainer를 사용하여 캐릭터 상태를 관리하세요. HasMatchingGameplayTag()로 스턴 상태에서 어빌리티 차단, HasAllMatchingGameplayTags()로 복합 조건(화속성+마법 사용자)을 체크하는 로직을 구현하세요.
Damage.Physical, Damage.Magical.Fire, Damage.Magical.Ice 태그와 Resistance.Fire, Resistance.Ice 태그를 조합하여, 공격 속성과 방어 속성에 따라 데미지가 가감되는 시스템을 GAS로 구현하세요.