UGameplayEffectExecutionCalculation 개요 및 활용 가이드

이 문서는 언리얼 엔진(UE)의 **Gameplay Ability System(GAS)**에서 UGameplayEffectExecutionCalculation을 활용하는 방법을 안내합니다. 역할과 목적, 사용 예제, 관련 클래스 간 관계, 그리고 최적화 및 커스터마이징 팁 등을 다룹니다.

1. 역할 및 목적

UGameplayEffectExecutionCalculation(이하 ExecutionCalculation)은 GameplayEffect에 특수한 실행 로직을 부여하기 위한 클래스입니다.
일반적인 Modifier만으로 처리하기 어려운 복잡한 계산 로직(예: 피해량 계산, 조건부 효과 발동)을 구현할 때 사용합니다.

  • 즉시(Instant) 또는 주기적(Periodic) 효과에서만 실행됩니다.
  • 효과가 발동될 때마다 한 번만 수행되어, 필요한 Attribute들을 캡처(capture) 후 계산에 활용합니다.
  • 스냅샷(Snapshot) 여부를 설정해 시점에 따라 다른 Attribute 값을 참조할 수 있습니다.
    - true이면 Spec 생성 시 값 고정,
    - false이면 실제 실행 시점의 최신 값 참조.

주의:
ExecutionCalculation은 클라이언트 예측이 불가하고, C++ 구현이 사실상 권장됩니다. (BlueprintNativeClass로서 Blueprint 상속은 가능하지만, 성능 및 복잡도 면에서 C++가 더 적합)

2. 주요 기능과 동작 원리

2.1 Execute_Implementation 함수

ExecutionCalculation은 Execute_Implementation 오버라이드로, 효과 발동 시점에 수행할 계산 로직을 정의합니다. 함수 인자는 다음과 같습니다:

  1. FGameplayEffectCustomExecutionParameters& ExecutionParams
    • 실행에 필요한 GameplayEffectSpec, Source/Target의 AbilitySystemComponent, 캡처된 태그 등을 조회 가능
    • 예:
      const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
      auto* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();
      
  2. FGameplayEffectCustomExecutionOutput& OutExecutionOutput
    • 계산 결과를 담아, 최종적으로 어떤 Attribute를 어떻게 변경할지 지정
    • 예:
      OutExecutionOutput.AddOutputModifier(
        FGameplayModifierEvaluatedData(Attribute, EGameplayModOp::Additive, Value)
      );
      

2.2 Attribute 캡처 & 스냅샷

  • Attribute 캡처: 사전에 등록된 캡처 정의에 따라 Source/Target의 Attribute 값을 가져옵니다.
    - ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(...)
  • 스냅샷: EGameplayEffectAttributeCaptureDefinition에 명시된 bool bSnapshot 값에 따라, Spec 생성 시점 또는 실행 시점 값을 사용합니다.

2.3 결과 적용 방식

  • OutExecutionOutput.AddOutputModifier(...)로 원하는 Attribute증가/감소 값을 지정합니다.
  • 하나의 ExecutionCalculation에서 여러 Attribute를 동시에 변경할 수도 있습니다.
  • MarkConditionalGameplayEffectsToTrigger(): Conditional GameplayEffect 조건을 만족하면 추가 효과를 발동하도록 지시합니다.
  • MarkStackCountHandledManually(), MarkGameplayCuesHandledManually(): 중첩(Stack)이나 GameplayCue를 수동으로 처리할 때 사용합니다. (특수 케이스)

3. 사용 예제 및 구현 방법

여기서는 데미지 계산 로직을 담은 ExecutionCalculation 예시를 간단히 살펴봅니다.

버전: Unreal Engine 5 기준

3.1 클래스 정의

C++에서 UGameplayEffectExecutionCalculation을 상속한 클래스를 생성합니다. 예:

UCLASS()
class UMyDamageExecutionCalculation : public UGameplayEffectExecutionCalculation
{
    GENERATED_BODY()

public:
    UMyDamageExecutionCalculation();

protected:
    virtual void Execute_Implementation(
      const FGameplayEffectCustomExecutionParameters& ExecutionParams,
      FGameplayEffectCustomExecutionOutput& OutExecutionOutput
    ) const override;
};

3.2 Attribute 캡처 정의

ExecutionCalculation에서는 어떤 Attribute를 캡처해 올지 정의해야 합니다.
일반적으로 정적 구조체를 만들어 AttackPower, DefensePower 등 필요한 속성을 지정한 뒤, 클래스 생성자에서 RelevantAttributesToCapture.Add(...)로 등록합니다.

struct FDamageStatics
{
    FGameplayEffectAttributeCaptureDefinition AttackPowerDef;
    FGameplayEffectAttributeCaptureDefinition DefensePowerDef;
    FGameplayEffectAttributeCaptureDefinition DamageDef;

    FDamageStatics()
    {
        AttackPowerDef = FGameplayEffectAttributeCaptureDefinition(
            UMyAttributeSet::GetAttackPowerAttribute(),
            EGameplayEffectAttributeCaptureSource::Source,
            true // 스냅샷 사용 여부
        );

        DefensePowerDef = FGameplayEffectAttributeCaptureDefinition(
            UMyAttributeSet::GetDefensePowerAttribute(),
            EGameplayEffectAttributeCaptureSource::Target,
            false
        );

        DamageDef = FGameplayEffectAttributeCaptureDefinition(
            UMyAttributeSet::GetDamageAttribute(),
            EGameplayEffectAttributeCaptureSource::Target,
            false
        );
    }
};

static const FDamageStatics& DamageStatics()
{
    static FDamageStatics Statics;
    return Statics;
}

// ---

UMyDamageExecutionCalculation::UMyDamageExecutionCalculation()
{
    RelevantAttributesToCapture.Add(DamageStatics().AttackPowerDef);
    RelevantAttributesToCapture.Add(DamageStatics().DefensePowerDef);
    RelevantAttributesToCapture.Add(DamageStatics().DamageDef);
}

3.3 Execute_Implementation 로직

필요한 값을 불러오고, 최종 값을 OutExecutionOutput에 반영합니다.

void UMyDamageExecutionCalculation::Execute_Implementation(
    const FGameplayEffectCustomExecutionParameters& ExecutionParams,
    FGameplayEffectCustomExecutionOutput& OutExecutionOutput
) const
{
    const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();

    FAggregatorEvaluateParameters EvalParams;
    EvalParams.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
    EvalParams.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

    // 캡처된 공격력, 방어력 가져오기
    float AttackPower = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
        DamageStatics().AttackPowerDef, EvalParams, AttackPower);

    float DefensePower = 0.f;
    ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(
        DamageStatics().DefensePowerDef, EvalParams, DefensePower);

    // SetByCaller로 전달된 추가 피해
    float BaseDamage = Spec.GetSetByCallerMagnitude(
        FGameplayTag::RequestGameplayTag("Data.Damage"),
        /*bWarnIfNotFound=*/false,
        0.f
    );

    // 간단한 예시: (공격력 + 추가 피해) - 방어력
    float DamageDone = FMath::Max(BaseDamage + AttackPower - DefensePower, 0.0f);

    if (DamageDone > 0.f)
    {
        OutExecutionOutput.AddOutputModifier(FGameplayModifierEvaluatedData(
            UMyAttributeSet::GetDamageAttribute(),
            EGameplayModOp::Additive,
            DamageDone
        ));

        // 예: 추가 효과(상태이상 등)가 있을 때만 발동
        OutExecutionOutput.MarkConditionalGameplayEffectsToTrigger();
    }
}

4. 다른 GAS 클래스와의 관계

클래스 설명

UGameplayEffect 지속시간, 수치 Modifier, 적용 대상을 정의하는 효과 오브젝트입니다. 여기서 Executions 배열에 ExecutionCalculation을 연결할 수 있습니다.
UAttributeSet 게임 플레이에 쓰이는 **속성(Attribute)**을 관리하는 클래스입니다. ExecutionCalculation은 여기서 캡처한 값으로 연산하고, 최종 결과도 다시 AttributeSet에 반영합니다.
UGameplayAbility 특정 액션(스킬)을 표현하는 클래스입니다. 일반적으로 이 Ability에서 GameplayEffect를 생성 및 적용합니다. 필요 시 SetByCaller 등으로 추가 데이터를 전달할 수 있습니다.
UAbilitySystemComponent GameplayEffect, Ability, Attribute 변화를 종합 관리하는 핵심 컴포넌트입니다. 즉시형(Instant) GE를 적용할 때 ExecutionCalculation이 실행됩니다.

5. 최적화 및 고급 활용 팁

  1. 캡처 정의 최소화
    • 불필요한 Attribute는 캡처하지 말고, 필요한 항목만 등록해 성능을 높입니다.
    • 캡처 정의를 여러 ExecutionCalculation에서 재사용할 때는 정적 구조체를 활용하세요.
  2. 스냅샷 기능
    • 시점별로 값이 달라질 수 있는 속성은 bSnapshot = false로 설정하여 실시간 캡처합니다.
    • 예측(예: 투사체 발사 후 도달까지 시간 차)이 필요한 경우 특정 속성만 미리 스냅샷을 활용해 정확도와 성능을 조절하세요.
  3. Blueprint vs C++
    • C++이 권장됩니다. (성능, 유지보수, 서버 로직 디버깅 측면)
    • Blueprint로 서브클래스화는 가능하나, 복잡한 수학 계산 등에 적합하지 않을 수 있습니다.
  4. SetByCaller & GameplayEffectContext
    • 동적 데이터(예: 무기 공격력, 콤보 스택)를 ExecutionCalculation에 전달할 때 유용합니다.
    • 필요 시 GameplayEffectContext를 확장하여 HitResult 등 추가 정보를 전달할 수 있습니다.
  5. 조건부 효과 & 수동 처리
    • MarkConditionalGameplayEffectsToTrigger(): 특정 조건이 충족될 때만 추가 효과를 발동.
    • MarkStackCountHandledManually(): 중첩(Stack) 로직을 수동 관리.
    • MarkGameplayCuesHandledManually(): Cue 처리를 별도 제어.
  6. 디버깅 & 테스트
    • ExecutionCalculation은 서버 전용이므로, Net 환경에서 디버깅 시 로그를 적극 활용하세요.
    • 단일 플레이어 테스트로 수치를 점검한 뒤 멀티플레이에서 검증하는 방식이 안전합니다.

6. 결론 및 참고 자료

UGameplayEffectExecutionCalculation을 통해 데미지, 치유, 상태 이상 등 복잡한 로직을 깔끔하게 관리할 수 있습니다. 하나의 클래스에서 공식과 로직을 집중적으로 다룰 수 있어, 밸런스 조정멀티플레이어 동기화에도 강력한 이점을 제공합니다.

참고 자료

이상의 내용을 참고하여, 적절한 UGameplayEffectExecutionCalculation을 구현하시기 바랍니다.
궁극적으로 더 직관적이고 유지보수성 높은 GAS 로직을 구성하는 데 많은 도움이 될 것입니다.


ⓒ 2023. (정보 출처: Unreal Engine 문서, 개발자 블로그, 샘플 프로젝트 등)

'개발일지(Unreal5) > GAS[Gameplay Ability System]' 카테고리의 다른 글

AbilitySystem  (0) 2025.03.26
AGameplayAbilityWorldReticle  (0) 2025.03.01
Unreal Engine GAS - Gameplay Cue  (0) 2025.02.14
FGameplayAbilityTargetData  (0) 2025.02.14
4.5 게임플레이 이펙트  (0) 2024.02.21

+ Recent posts