AttributeSet만 쓰는 구조

변수의 Set

1. ASC 사용

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = GAS, meta = (AllowPrivateAccess = "true"))
    TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;

2. ASC 초기화, Attribute 변경 델리게이트 바인딩

// Actor 설정, Owner Actor와 Avatar Actor
AbilitySystemComponent->InitAbilityActorInfo(this, this);
// Delegate 설정
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPXHealthSet::GetHealthAttribute()).AddUObject(this, &ACombatCharacter::HandleHealthAttributeChanged);
AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(UPXHealthSet::GetMaxHealthAttribute()).AddUObject(this, &ACombatCharacter::HandleHealthAttributeChanged);

3. AttributeSet 설정

void ACombatCharacter::HandleHealthAttributeChanged(const FOnAttributeChangeData& ChangeData)
{
    if (AbilitySystemComponent && HealthSet && HealthSet->GetHealth() > HealthSet->GetMaxHealth())
    {
        AbilitySystemComponent->SetNumericAttributeBase(UPXHealthSet::GetHealthAttribute(), HealthSet->GetMaxHealth());
    }

    TXCombatComponent->SetMaxHP(HealthSet->GetMaxHealth());
    TXCombatComponent->SetCurrentHP(HealthSet->GetHealth());
    TXCombatComponent->UpdateHPBar();

    if (ChangeData.OldValue > 0.0f && ChangeData.NewValue <= 0.0f)
    {
        HandleDeath();
    }
}

4. 기존 CombatComponent에서 계산하던 체력 로직을 ASC가 하게 변경

//////////////////////////////////////
////////////// 기존 로직 //////////////
//////////////////////////////////////

float UPXCombatComponent::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
    // only process damage if the character is still alive
    if (CurrentHP <= 0.0f)
    {
        return 0.0f;
    }

    if (nullptr == OwnerCharacter)
        return 0.0f;

    // reduce the current HP
    CurrentHP -= Damage;

    //have we run out of HP?
    if (CurrentHP <= 0.0f)
    {
        // die
        OwnerCharacter->HandleDeath();
    }
    else
    {
        // update the life bar
        UpdateHPBar();

        PlayHitReaction();
    }
    return Damage;
}

//////////////////////////////////////
////////////// 바뀐 로직 //////////////
//////////////////////////////////////

float ACombatCharacter::TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
    if (Damage <= 0.0f || !HealthSet || HealthSet->GetHealth() <= 0.0f)
    {
        return 0.0f;
    }

    const float PreviousHealth = HealthSet->GetHealth();
    AbilitySystemComponent->ApplyModToAttribute(UPXHealthSet::GetHealthAttribute(), EGameplayModOp::Additive, -Damage);
    const float ActualDamage = FMath::Max(0.0f, PreviousHealth - HealthSet->GetHealth());
    if (ActualDamage > 0.0f && HealthSet->GetHealth() > 0.0f && TXCombatComponent)
    {
        TXCombatComponent->PlayHitReaction();
    }

    return ActualDamage;
}

흐름 ApplyDamage → TakeDamage → GAS Health 감소 → Attribute 변경 → 기존 CombatComponent HP/UI 동기화 → 0 이하이면 HandleDeath

Attribute + GameplayAbility + GameplayTag 구조

AttributeSet
= Health, MaxHealth 같은 수치 데이터

GameplayAbility
= 공격, 피격, 회복, 대시, 사망 같은 행동

GameplayTag
= Dead, Stunned, Attacking, HitReact, Cooldown 같은 상태/조건

AbilitySystemComponent
= Ability 부여/실행, Attribute 변경, Tag 관리


1. ASC + AttributeSet 사용

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = GAS, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UAbilitySystemComponent> AbilitySystemComponent;

UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = GAS, meta = (AllowPrivateAccess = "true"))
TObjectPtr<UPXHealthSet> HealthSet;

Character가 ASC를 가진다.

ACombatCharacter::ACombatCharacter()
{
    AbilitySystemComponent = CreateDefaultSubobject<UAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
    HealthSet = CreateDefaultSubobject<UPXHealthSet>(TEXT("HealthSet"));
}

2. GameplayTag 정의

상태와 Ability 분류를 GameplayTag로 만든다.

// PXGameplayTags.h

#pragma once

#include "NativeGameplayTags.h"

UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_State_Dead);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_State_HitReact);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_State_Attacking);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_State_Stunned);

UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Ability_Attack);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Ability_HitReact);
UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_Ability_Death);

UE_DECLARE_GAMEPLAY_TAG_EXTERN(TAG_InputTag_Attack);
// PXGameplayTags.cpp

#include "PXGameplayTags.h"

UE_DEFINE_GAMEPLAY_TAG(TAG_State_Dead, "State.Dead"); // 죽은 상태
UE_DEFINE_GAMEPLAY_TAG(TAG_State_HitReact, "State.HitReact"); // 피격 반응 중
UE_DEFINE_GAMEPLAY_TAG(TAG_State_Attacking, "State.Attacking"); // 공격 중
UE_DEFINE_GAMEPLAY_TAG(TAG_State_Stunned, "State.Stunned"); // 

UE_DEFINE_GAMEPLAY_TAG(TAG_Ability_Attack, "Ability.Attack"); //  공격 Ability 분류
UE_DEFINE_GAMEPLAY_TAG(TAG_Ability_HitReact, "Ability.HitReact"); // 
UE_DEFINE_GAMEPLAY_TAG(TAG_Ability_Death, "Ability.Death"); // 

UE_DEFINE_GAMEPLAY_TAG(TAG_InputTag_Attack, "InputTag.Ability.Attack"); // 입력과 Ability를 연결하기 위한 태그

3. ASC 초기화, Attribute Delegate 바인딩, Tag Delegate 바인딩

void ACombatCharacter::BeginPlay()
{
    Super::BeginPlay();

    // Owner Actor와 Avatar Actor 설정
    AbilitySystemComponent->InitAbilityActorInfo(this, this);

    // Attribute 변경 Delegate
    AbilitySystemComponent
        ->GetGameplayAttributeValueChangeDelegate(UPXHealthSet::GetHealthAttribute())
        .AddUObject(this, &ACombatCharacter::HandleHealthAttributeChanged);

    AbilitySystemComponent
        ->GetGameplayAttributeValueChangeDelegate(UPXHealthSet::GetMaxHealthAttribute())
        .AddUObject(this, &ACombatCharacter::HandleHealthAttributeChanged);

    // GameplayTag 변경 Delegate
    AbilitySystemComponent
        ->RegisterGameplayTagEvent(TAG_State_Dead, EGameplayTagEventType::NewOrRemoved)
        .AddUObject(this, &ACombatCharacter::HandleDeadTagChanged);

    AbilitySystemComponent
        ->RegisterGameplayTagEvent(TAG_State_HitReact, EGameplayTagEventType::NewOrRemoved)
        .AddUObject(this, &ACombatCharacter::HandleHitReactTagChanged);

    // Ability 부여
    GiveDefaultAbilities();
}

4. AttributeSet 설정

체력 값은 AttributeSet에서 관리한다.

UCLASS()
class UPXHealthSet : public UAttributeSet
{
    GENERATED_BODY()

public:
    UPROPERTY(BlueprintReadOnly, Category = Health)
    FGameplayAttributeData Health;

    UPROPERTY(BlueprintReadOnly, Category = Health)
    FGameplayAttributeData MaxHealth;

    static FGameplayAttribute GetHealthAttribute();
    static FGameplayAttribute GetMaxHealthAttribute();

    float GetHealth() const { return Health.GetCurrentValue(); }
    float GetMaxHealth() const { return MaxHealth.GetCurrentValue(); }

    void SetHealth(float NewValue) { Health.SetCurrentValue(NewValue); }
    void SetMaxHealth(float NewValue) { MaxHealth.SetCurrentValue(NewValue); }

    virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override
    {
        Super::PreAttributeChange(Attribute, NewValue);

        if (Attribute == GetHealthAttribute())
        {
            NewValue = FMath::Clamp(NewValue, 0.0f, GetMaxHealth());
        }

        if (Attribute == GetMaxHealthAttribute())
        {
            NewValue = FMath::Max(0.0f, NewValue);
        }
    }
};

기존에는 Character의 Delegate에서 Health를 Clamp

AttributeSet 내부에서 Clamp하는 구조


5. Attribute 변경 처리

체력이 바뀌면 Character가 반응한다.

void ACombatCharacter::HandleHealthAttributeChanged(const FOnAttributeChangeData& ChangeData)
{
    if (!AbilitySystemComponent || !HealthSet)
    {
        return;
    }

    const float CurrentHealth = HealthSet->GetHealth();
    const float MaxHealth = HealthSet->GetMaxHealth();

    TXCombatComponent->SetMaxHP(MaxHealth);
    TXCombatComponent->SetCurrentHP(CurrentHealth);
    TXCombatComponent->UpdateHPBar();

    // 살아있다가 0 이하가 됐을 때만 사망 처리
    if (ChangeData.OldValue > 0.0f && ChangeData.NewValue <= 0.0f)
    {
        // 죽음 상태 태그 부여
        AbilitySystemComponent->AddLooseGameplayTag(TAG_State_Dead);

        // Death Ability 실행
        AbilitySystemComponent->TryActivateAbilitiesByTag(
            FGameplayTagContainer(TAG_Ability_Death)
        );
    }
}

이제 HandleDeath()를 직접 호출하는 대신,
State.Dead 태그를 부여하고 GA_Death를 실행한다.

Health <= 0
→ State.Dead 태그 부여
→ GA_Death 실행
→ 사망 애니메이션 / 충돌 제거 / 입력 차단 처리

6. Ability 부여

캐릭터가 시작될 때 기본 Ability를 ASC에 부여한다.

UPROPERTY(EditDefaultsOnly, Category = GAS)
TSubclassOf<UGameplayAbility> AttackAbilityClass;

UPROPERTY(EditDefaultsOnly, Category = GAS)
TSubclassOf<UGameplayAbility> HitReactAbilityClass;

UPROPERTY(EditDefaultsOnly, Category = GAS)
TSubclassOf<UGameplayAbility> DeathAbilityClass;
void ACombatCharacter::GiveDefaultAbilities()
{
    if (!AbilitySystemComponent || !HasAuthority())
    {
        return;
    }

    if (AttackAbilityClass)
    {
        FGameplayAbilitySpec AttackSpec(AttackAbilityClass, 1);
        AttackSpec.GetDynamicSpecSourceTags().AddTag(TAG_InputTag_Attack);
        AttackSpec.GetDynamicSpecSourceTags().AddTag(TAG_Ability_Attack);

        AbilitySystemComponent->GiveAbility(AttackSpec);
    }

    if (HitReactAbilityClass)
    {
        FGameplayAbilitySpec HitReactSpec(HitReactAbilityClass, 1);
        HitReactSpec.GetDynamicSpecSourceTags().AddTag(TAG_Ability_HitReact);

        AbilitySystemComponent->GiveAbility(HitReactSpec);
    }

    if (DeathAbilityClass)
    {
        FGameplayAbilitySpec DeathSpec(DeathAbilityClass, 1);
        DeathSpec.GetDynamicSpecSourceTags().AddTag(TAG_Ability_Death);

        AbilitySystemComponent->GiveAbility(DeathSpec);
    }
}
ASC
 └─ ActivatableAbilities
      ├─ GA_Attack
      │   ├─ Ability.Attack
      │   └─ InputTag.Ability.Attack
      │
      ├─ GA_HitReact
      │   └─ Ability.HitReact
      │
      └─ GA_Death
          └─ Ability.Death

7. 입력으로 GameplayAbility 실행

기존에는 입력에서 Character나 CombatComponent 함수를 직접 호출했다.

TXCombatComponent->Attack();

GAS 구조에서는 입력 태그를 기준으로 Ability를 찾고 실행한다.

void ACombatCharacter::Input_AttackPressed()
{
    if (!AbilitySystemComponent)
    {
        return;
    }

    // 죽었거나 기절 상태면 공격 불가
    if (AbilitySystemComponent->HasMatchingGameplayTag(TAG_State_Dead) ||
        AbilitySystemComponent->HasMatchingGameplayTag(TAG_State_Stunned))
    {
        return;
    }

    for (const FGameplayAbilitySpec& AbilitySpec : AbilitySystemComponent->GetActivatableAbilities())
    {
        if (AbilitySpec.Ability &&
            AbilitySpec.GetDynamicSpecSourceTags().HasTagExact(TAG_InputTag_Attack))
        {
            AbilitySystemComponent->TryActivateAbility(AbilitySpec.Handle);
            break;
        }
    }
}
Attack 입력
→ InputTag.Ability.Attack을 가진 AbilitySpec 검색
→ TryActivateAbility()
→ GA_Attack::CanActivateAbility()
→ GA_Attack::ActivateAbility()

8. GameplayAbility에서 GameplayTag 조건 검사

공격 Ability는 죽음/기절/피격 중이면 실행되지 않게 한다.

UCLASS()
class UGA_Attack : public UGameplayAbility
{
    GENERATED_BODY()

public:
    UGA_Attack()
    {
        AbilityTags.AddTag(TAG_Ability_Attack);

        ActivationBlockedTags.AddTag(TAG_State_Dead);
        ActivationBlockedTags.AddTag(TAG_State_Stunned);
        ActivationBlockedTags.AddTag(TAG_State_HitReact);
        ActivationBlockedTags.AddTag(TAG_State_Attacking);
    }

    virtual bool CanActivateAbility(
        const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayTagContainer* SourceTags,
        const FGameplayTagContainer* TargetTags,
        FGameplayTagContainer* OptionalRelevantTags) const override
    {
        if (!Super::CanActivateAbility(Handle, ActorInfo, SourceTags, TargetTags, OptionalRelevantTags))
        {
            return false;
        }

        return ActorInfo && ActorInfo->AvatarActor.IsValid();
    }

    virtual void ActivateAbility(
        const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        const FGameplayEventData* TriggerEventData) override
    {
        if (!CommitAbility(Handle, ActorInfo, ActivationInfo))
        {
            EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
            return;
        }

        UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get();
        ACombatCharacter* Character = Cast<ACombatCharacter>(ActorInfo->AvatarActor.Get());

        if (!ASC || !Character)
        {
            EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
            return;
        }

        // 공격 상태 태그 부여
        ASC->AddLooseGameplayTag(TAG_State_Attacking);

        // 기존 CombatComponent 공격 로직 호출 가능
        Character->GetTXCombatComponent()->PlayAttackMontage();

        // Montage 끝나는 시점이나 AnimNotify 시점에서 EndAbility 호출
        // 예시에서는 바로 종료 처리
        ASC->RemoveLooseGameplayTag(TAG_State_Attacking);

        EndAbility(Handle, ActorInfo, ActivationInfo, false, false);
    }
};

이제 공격 중 상태는 bool이 아니라 State.Attacking 태그로 표현한다.

GA_Attack 시작
→ State.Attacking 태그 부여

GA_Attack 종료
→ State.Attacking 태그 제거

9. 데미지 처리

//////////////////////////////////////
////////////// 기존 로직 //////////////
//////////////////////////////////////

float UPXCombatComponent::TakeDamage(float Damage, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
    if (CurrentHP <= 0.0f)
    {
        return 0.0f;
    }

    if (nullptr == OwnerCharacter)
    {
        return 0.0f;
    }

    CurrentHP -= Damage;

    if (CurrentHP <= 0.0f)
    {
        OwnerCharacter->HandleDeath();
    }
    else
    {
        UpdateHPBar();
        PlayHitReaction();
    }

    return Damage;
}

데미지 수치 변경은 ASC를 통해 Attribute를 수정한다.
피격 반응은 직접 호출하지 않고 GA_HitReact로 넘긴다.

//////////////////////////////////////
////////////// 바뀐 로직 //////////////
//////////////////////////////////////

float ACombatCharacter::TakeDamage(
    float Damage,
    FDamageEvent const& DamageEvent,
    AController* EventInstigator,
    AActor* DamageCauser)
{
    if (Damage <= 0.0f || !AbilitySystemComponent || !HealthSet)
    {
        return 0.0f;
    }

    if (AbilitySystemComponent->HasMatchingGameplayTag(TAG_State_Dead))
    {
        return 0.0f;
    }

    const float PreviousHealth = HealthSet->GetHealth();

    // AttributeSet의 Health 변경
    AbilitySystemComponent->ApplyModToAttribute(
        UPXHealthSet::GetHealthAttribute(),
        EGameplayModOp::Additive,
        -Damage
    );

    const float ActualDamage = FMath::Max(0.0f, PreviousHealth - HealthSet->GetHealth());

    if (ActualDamage > 0.0f && HealthSet->GetHealth() > 0.0f)
    {
        // 피격 반응도 Ability로 실행
        AbilitySystemComponent->TryActivateAbilitiesByTag(
            FGameplayTagContainer(TAG_Ability_HitReact)
        );
    }

    return ActualDamage;
}
TakeDamage()
→ ASC.ApplyModToAttribute(Health, -Damage)
→ Health Attribute 변경 Delegate 호출
→ HPBar 갱신
→ Health <= 0이면 State.Dead 부여 + GA_Death 실행
→ 살아있으면 GA_HitReact 실행

10. HitReact Ability

피격 반응도 CombatComponent에서 직접 재생하지 않고 Ability로 만든다.

UCLASS()
class UGA_HitReact : public UGameplayAbility
{
    GENERATED_BODY()

public:
    UGA_HitReact()
    {
        AbilityTags.AddTag(TAG_Ability_HitReact);

        ActivationBlockedTags.AddTag(TAG_State_Dead);
    }

    virtual void ActivateAbility(
        const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        const FGameplayEventData* TriggerEventData) override
    {
        UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get();
        ACombatCharacter* Character = Cast<ACombatCharacter>(ActorInfo->AvatarActor.Get());

        if (!ASC || !Character)
        {
            EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
            return;
        }

        ASC->AddLooseGameplayTag(TAG_State_HitReact);

        Character->GetTXCombatComponent()->PlayHitReaction();

        // 실제로는 Montage 종료 Delegate에서 제거하는 게 좋음
        ASC->RemoveLooseGameplayTag(TAG_State_HitReact);

        EndAbility(Handle, ActorInfo, ActivationInfo, false, false);
    }
};

이제 피격 중인지 여부는 State.HitReact 태그로 확인한다.

if (AbilitySystemComponent->HasMatchingGameplayTag(TAG_State_HitReact))
{
    // 피격 중
}

11. Death Ability

죽음 처리도 Character의 일반 함수 하나로 끝내지 않고 Ability로 만든다.

UCLASS()
class UGA_Death : public UGameplayAbility
{
    GENERATED_BODY()

public:
    UGA_Death()
    {
        AbilityTags.AddTag(TAG_Ability_Death);

        // 죽음 Ability는 Dead 상태에서 실행될 수 있어야 한다.
        // 그래서 State.Dead를 BlockedTags에 넣지 않는다.
    }

    virtual void ActivateAbility(
        const FGameplayAbilitySpecHandle Handle,
        const FGameplayAbilityActorInfo* ActorInfo,
        const FGameplayAbilityActivationInfo ActivationInfo,
        const FGameplayEventData* TriggerEventData) override
    {
        UAbilitySystemComponent* ASC = ActorInfo->AbilitySystemComponent.Get();
        ACombatCharacter* Character = Cast<ACombatCharacter>(ActorInfo->AvatarActor.Get());

        if (!ASC || !Character)
        {
            EndAbility(Handle, ActorInfo, ActivationInfo, true, true);
            return;
        }

        ASC->AddLooseGameplayTag(TAG_State_Dead);

        Character->DisableInput(nullptr);
        Character->GetTXCombatComponent()->UpdateHPBar();
        Character->GetTXCombatComponent()->PlayDeathMontage();

        // 충돌 제거, AI 정지, Ragdoll 등 처리
        // Character->GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);

        EndAbility(Handle, ActorInfo, ActivationInfo, false, false);
    }
};

죽음 상태는 이제 bool이 아니라 태그로 판단한다.

if (AbilitySystemComponent->HasMatchingGameplayTag(TAG_State_Dead))
{
    return;
}

12. GameplayTag 변경에 따른 반응

태그 변경 Delegate로 상태 변화를 감지할 수 있다.

void ACombatCharacter::HandleDeadTagChanged(const FGameplayTag Tag, int32 NewCount)
{
    const bool bIsDead = NewCount > 0;

    if (bIsDead)
    {
        // 입력 차단, UI 변경, AI 정지 등
    }
    else
    {
        // 부활 처리
    }
}

void ACombatCharacter::HandleHitReactTagChanged(const FGameplayTag Tag, int32 NewCount)
{
    const bool bIsHitReacting = NewCount > 0;

    if (TXCombatComponent)
    {
        TXCombatComponent->SetIsHitReacting(bIsHitReacting);
    }
}

흐름

공격

Input Attack
→ InputTag.Ability.Attack
→ ASC에서 해당 InputTag를 가진 FGameplayAbilitySpec 검색
→ TryActivateAbility()
→ GA_Attack 실행
→ State.Attacking 태그 부여
→ 공격 Montage 재생
→ AnimNotify에서 상대 TakeDamage 호출
→ 공격 종료 시 State.Attacking 제거

데미지

TakeDamage(Damage)
→ ASC.ApplyModToAttribute(Health, -Damage)
→ Health Attribute 변경
→ HandleHealthAttributeChanged()
→ HPBar 갱신
→ Health > 0이면 GA_HitReact 실행
→ Health <= 0이면 State.Dead 부여 + GA_Death 실행

피격

GA_HitReact 실행
→ State.HitReact 태그 부여
→ HitReaction Montage 재생
→ Montage 종료
→ State.HitReact 태그 제거
→ Ability 종료

사망

Health <= 0
→ State.Dead 태그 부여
→ GA_Death 실행
→ Death Montage 재생
→ 입력 차단
→ Collision 제거
→ Ability 종료

최종 구조

ACombatCharacter
 ├─ AbilitySystemComponent
 ├─ HealthSet
 └─ TXCombatComponent

UPXHealthSet
 ├─ Health
 └─ MaxHealth

GameplayTags
 ├─ State.Dead
 ├─ State.HitReact
 ├─ State.Attacking
 ├─ State.Stunned
 ├─ Ability.Attack
 ├─ Ability.HitReact
 ├─ Ability.Death
 └─ InputTag.Ability.Attack

GameplayAbilities
 ├─ GA_Attack
 ├─ GA_HitReact
 └─ GA_Death

CombatComponent
 ├─ HPBar 갱신
 ├─ Montage 재생
 └─ 표현 처리

ASC
 ├─ Attribute 변경
 ├─ Ability 실행
 ├─ Tag 부여/제거
 └─ AbilitySpec 관리

구조 요약

AttributeSet
= 체력 같은 수치 보관

GameplayAbility
= 공격, 피격, 사망 같은 행동 실행

GameplayTag
= 죽음, 피격 중, 공격 중 같은 상태 표현

ASC
= 수치 변경, Ability 실행, Tag 상태 관리

CombatComponent
= UI와 연출 처리
반응형

'Unreal Engine' 카테고리의 다른 글

UE Game play Framework 2  (0) 2026.06.18
UE Game play Framework 1  (0) 2026.06.17
GAS 1  (0) 2026.06.15
SmartObject 1  (0) 2026.06.12
GameplayMessageRouter Plugin 2  (1) 2026.06.11