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 오버라이드로, 효과 발동 시점에 수행할 계산 로직을 정의합니다. 함수 인자는 다음과 같습니다:
- FGameplayEffectCustomExecutionParameters& ExecutionParams
- 실행에 필요한 GameplayEffectSpec, Source/Target의 AbilitySystemComponent, 캡처된 태그 등을 조회 가능
- 예:
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec(); auto* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();
- 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. 최적화 및 고급 활용 팁
- 캡처 정의 최소화
- 불필요한 Attribute는 캡처하지 말고, 필요한 항목만 등록해 성능을 높입니다.
- 캡처 정의를 여러 ExecutionCalculation에서 재사용할 때는 정적 구조체를 활용하세요.
- 스냅샷 기능
- 시점별로 값이 달라질 수 있는 속성은 bSnapshot = false로 설정하여 실시간 캡처합니다.
- 예측(예: 투사체 발사 후 도달까지 시간 차)이 필요한 경우 특정 속성만 미리 스냅샷을 활용해 정확도와 성능을 조절하세요.
- Blueprint vs C++
- C++이 권장됩니다. (성능, 유지보수, 서버 로직 디버깅 측면)
- Blueprint로 서브클래스화는 가능하나, 복잡한 수학 계산 등에 적합하지 않을 수 있습니다.
- SetByCaller & GameplayEffectContext
- 동적 데이터(예: 무기 공격력, 콤보 스택)를 ExecutionCalculation에 전달할 때 유용합니다.
- 필요 시 GameplayEffectContext를 확장하여 HitResult 등 추가 정보를 전달할 수 있습니다.
- 조건부 효과 & 수동 처리
- MarkConditionalGameplayEffectsToTrigger(): 특정 조건이 충족될 때만 추가 효과를 발동.
- MarkStackCountHandledManually(): 중첩(Stack) 로직을 수동 관리.
- MarkGameplayCuesHandledManually(): Cue 처리를 별도 제어.
- 디버깅 & 테스트
- 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 |