
Gameplay Attribute의 값을 변화시킬 수 있는 Gameplay Effect에는 여러 가지 Modifier Magnitude를 설정할 수 있다.
기존에 만들어진 Curve Table을 이용할 수 있는 Scalable float 옵션, 다른 어트리뷰트에 연산을 더한 값을 사용할 수 있는 Attribute Based 옵션, 호출하는 쪽에서 값을 정할 수 있는 Set by caller 옵션 등이 있다. 그리고 이번 글에서 다룰 Custom Calculation Class를 사용하면 프로그래머가 여러 어트리뷰트를 사용해서 임의로 계산식을 만들 수 있는 강력한 기능을 사용할 수 있다.
굉장히 긴 이름의 이 클래스를 상속하는 하위 클래스를 만듦으로써 계산식을 커스텀할 수 있다.
이 클래스의 맨 아래를 보면 다음과 같은 매크로식을 발견할 수 있다.
#define DECLARE_ATTRIBUTE_CAPTUREDEF(P) \
FProperty* P##Property; \
FGameplayEffectAttributeCaptureDefinition P##Def; \
#define DEFINE_ATTRIBUTE_CAPTUREDEF(S, P, T, B) \
{ \
P##Property = FindFieldChecked<FProperty>(S::StaticClass(), GET_MEMBER_NAME_CHECKED(S, P)); \
P##Def = FGameplayEffectAttributeCaptureDefinition(P##Property, EGameplayEffectAttributeCaptureSource::T, B); \
}
P 라는 파라미터를 받아서 P##Property, P##Def 이런 식으로 변수명에 사용하고 있는 것을 볼 수 있는데, # 기호 두 개를 이어 붙인 ## 형태를 사용하면 마치 문자열을 이어 붙이듯이 파라미터를 이름에 사용할 수 있다.
AttributeSet에서 GAMEPLAYATTRIBUTE_VALUE_GETTER 매크로에서 사용해 본 적이 있다.
#define GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
FORCEINLINE float Get##PropertyName() const \
{ \
return PropertyName.GetCurrentValue(); \
}
예를 들어 PropertyName에 Health 를 입력해서 선언하면 Get##PropertyName() 에 의해 GetHealth() 라는 getter 함수를 쉽게 생성할 수 있다.
이렇게 생성된 프로퍼티를 관리/접근할 수 있도록 static 변수를 만들어 주고, 계산에 사용될 어트리뷰트를 저장하는 TArray<FGameplayEffectAttributeCaptureDefinition>인 RelevantAttributesToCapture에 Definition을 추가한다.
static const AuraDamageStatics& DamageStatics()
{
static AuraDamageStatics DStatics;
return DStatics;
}
Gameplay Attribute가 실행되면 UGameplayEffectExecutionCalculation::Execute() 함수가 실행된다.
이 함수는 두 개의 파라미터를 가지며 하나는 FGameplayEffectCustomExecutionParameters 파라미터이고, 하나는 FGameplayEffectCustomExecutionOutput 이다. 함수의 선언부로 이동해 주석을 확인해 보면 ExecutionParams는 Custom execution calculation에 필요한 여러 파라미터들을 담고 있는 구조체임을 알 수 있다. 그리고 OutExecutionOutput은 실행의 결과를 담은 구조체임을 알 수 있다.
/**
* Called whenever the owning gameplay effect is executed. Allowed to do essentially whatever is desired, including generating new
* modifiers to instantly execute as well.
*
* @note: Native subclasses should override the auto-generated Execute_Implementation function and NOT this one.
*
* @param ExecutionParams Parameters for the custom execution calculation
* @param OutExecutionOutput [OUT] Output data populated by the execution detailing further behavior or results of the execution
*/
UFUNCTION(BlueprintNativeEvent, Category="Calculation")
void Execute(const FGameplayEffectCustomExecutionParameters& ExecutionParams, FGameplayEffectCustomExecutionOutput& OutExecutionOutput) const;
Execution Parameter 구조체에는 여러 멤버 변수들이 들어 있고,

output 구조체에는 결과를 나타내는 여러 멤버들이 들어 있다.

FAggregatorEvaluateParameters EvalParam;
EvalParam.SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
EvalParam.TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
float Armor = 0.f;
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorDef, EvalParam, Armor);
Armor = FMath::Max<float>(0.f, Armor);
FGameplayModifierEvaluatedData EvaluatedData(DamageStatics().ArmorProperty, EGameplayModOp::Additive, Armor);
OutExecutionOutput.AddOutputModifier(EvaluatedData);
AttemptCalculateCapturedAttributeMagnitude()를 통해 Armor Definition의 값을 로컬 변수로 가져오고, 이를 토대로 EvaluatedData를 만들어 Output에 추가할 수 있다.

위 사진과 같이 Execution에 해당 클래스를 Calculation Class 자리에 추가해 주면, 이 GE가 실행될 때 해당 클래스에서 오버라이드한 Execute() 함수가 실행된다.