PART 4 - 강의 6/8

GameplayTags 시스템

계층적 태그로 게임 상태와 조건 관리

01

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
02

C++에서 태그 정의

네이티브 GameplayTag 선언

MyGameplayTags.h #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); }
MyGameplayTags.cpp #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"); }
03

태그 조건 설정

어빌리티 활성화 조건으로 태그 사용

GameplayAbility 태그 설정 // 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.MeleeAbility.Attack.Ranged 모두 매칭됩니다. 부모 태그는 자식 태그를 포함합니다.

04

태그 런타임 조작

코드에서 태그 추가/제거/조회

C++ // 태그 추가 (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(); } }
05

태그 기반 로직

게임플레이 조건부 처리

C++ // 데미지 타입에 따른 처리 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; }
SUMMARY

핵심 요약

  • GameplayTags는 계층적 식별자로 상태와 조건 관리에 최적
  • UE_DECLARE/DEFINE_GAMEPLAY_TAG로 C++에서 태그 정의
  • ActivationBlockedTags로 어빌리티 활성화 조건 설정
  • AddLooseGameplayTag로 런타임 태그 조작
  • RegisterGameplayTagEvent로 태그 변경 감지
PRACTICE

도전 과제

배운 내용을 직접 실습해보세요

실습 1: RPG GameplayTag 계층 설계

DefaultGameplayTags.ini에 State.Dead, State.Stunned, Ability.Skill.Fire, Ability.Skill.Ice, Buff.AttackUp, Debuff.Poison 등의 태그 계층을 정의하세요. C++에서 FGameplayTag::RequestGameplayTag()로 태그를 얻으세요.

실습 2: 태그 기반 조건 시스템

FGameplayTagContainer를 사용하여 캐릭터 상태를 관리하세요. HasMatchingGameplayTag()로 스턴 상태에서 어빌리티 차단, HasAllMatchingGameplayTags()로 복합 조건(화속성+마법 사용자)을 체크하는 로직을 구현하세요.

심화 과제: 태그 기반 데미지 시스템

Damage.Physical, Damage.Magical.Fire, Damage.Magical.Ice 태그와 Resistance.Fire, Resistance.Ice 태그를 조합하여, 공격 속성과 방어 속성에 따라 데미지가 가감되는 시스템을 GAS로 구현하세요.