본문 바로가기

게임프로그래밍/실습2

[실습2] 51. UGameplayEffectExecutionCalculation 클래스

1. UGameplayEffectExecutionCalculation

 

데미지 효과를 커스텀으로 계산하기 위하여 UGameplayEffectExecutionCalculation 커스텀 클래스를 구현하려고 한다.

 

해당 클래스를 만들고 아래 함수를 오버라이드 한다.

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

 

해당 함수는 GameplayEffect의 실행로직을 커스텀화 할때 사용하는 함수라고 한다.

 


매개변수 설명

  1. const FGameplayEffectCustomExecutionParameters& ExecutionParams
    • 역할: 실행에 필요한 입력 데이터를 제공합니다.
    • 내용:
      • ExecutionParams는 효과를 계산할 때 필요한 정보와 컨텍스트(맥락)를 포함합니다.
      • 포함된 주요 데이터:
        • Source(효과를 발생시킨 대상)
        • Target(효과를 받는 대상)
        • 게임플레이 태그, 속성(Attributes), 기타 데이터.
    • 이 데이터를 통해 효과 계산의 기반 데이터를 가져올 수 있습니다.
  2. FGameplayEffectCustomExecutionOutput& OutExecutionOutput
    • 역할: 실행 결과를 출력 데이터로 반환합니다.
    • 내용:
      • 이 매개변수는 효과 계산의 결과(속성 변화, 실행 결과 등)를 저장합니다.
      • 개발자는 이 구조체를 사용해 계산된 결과를 설정하거나 속성을 업데이트합니다.

해당 함수를 구현하도록 하자.

 

소스와 타겟의 ASC를 얻자. 
const UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent();
const UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();

 

이를 통해서 아바타 액터도 구할 수 있다.

const AActor* SourceAvatar = SourceASC ? SourceASC->GetAvatarActor() : nullptr;
const AActor* TargetAvatar = TargetASC ? TargetASC->GetAvatarActor() : nullptr;

const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();

 

이제 속성들을 캡쳐해서 이것에 영향을 미쳐야 한다.

 


2. 속성 캡쳐하기

 

속성을 저장할 구조체를 만들 것이다.

 

struct AuraDamageStatics
{
    DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);
    
    AuraDamageStatics()
    {
       
    }
};

 

DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);

 

해당 매크로의 정의는 아래와 같다.

#define DECLARE_ATTRIBUTE_CAPTUREDEF(P) \
    FProperty* P##Property; \
    FGameplayEffectAttributeCaptureDefinition P##Def; \

 

해당 매크로를 사용하면 P의 이름을 가진 속성이 캡쳐가 된다. 정확히는 캡쳐할 속성이 선언이 된다. 선언만 된 것이기 때문에 매크로로 캡쳐된 속성은 반드시 생성자에서 초기화해야한다.

 

이때 사용하는 매크로가 아래의 매크로이다. 

 

#define DEFINE_ATTRIBUTE_CAPTUREDEF(S, P, T, B) \

 


DEFINE_ATTRIBUTE_CAPTUREDEF

  • DECLARE_ATTRIBUTE_CAPTUREDEF로 선언한 속성을 정의하고 초기화합니다.
  • 예: Source/Target 객체에서 속성을 캡처할지 여부 등을 설정.
#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); \
}
  • S : 속성이 정의된 클래스.
  • P : 캡처할 속성 이름.
  • T : Source 또는 Target(속성을 가져올 객체 지정).
  • B : 캡처 시점에서 값을 스냅샷처럼 고정할지 여부.

아래와 같이 사용될 수 있다.

struct AuraDamageStatics
{
    DECLARE_ATTRIBUTE_CAPTUREDEF(Armor);
    
    
    AuraDamageStatics()
    {
       DEFINE_ATTRIBUTE_CAPTUREDEF(UAuraAttributeSet, Armor, Target, false);
    }
};

 

그리고 이 스태틱 구조체를 반환할 함수를 정의하자.

 

static const AuraDamageStatics& DamageStatics()
{
    static AuraDamageStatics DStatics;
    return DStatics;
}

 

데미지 스태틱 구조체는 런타임 중 하나만 존재하게 될 것이다.

 

속성을 캡쳐했으면 캡쳐 정의를 저장하는 배열에 저장되어야 할 것이다.

 

UExecCalc_Damage::UExecCalc_Damage()
{
    RelevantAttributesToCapture.Add(DamageStatics().ArmorDef);
}

 

이렇게 필요한 속성들을 캡쳐할 수 있다.

 

이제 이 캡쳐된 아머를 사용할 수 있어야 할 것이다.

 

const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();
FAggregatorEvaluateParameters EvaluationParameters;
EvaluationParameters.SourceTags = SourceTags;
EvaluationParameters.TargetTags = TargetTags;

float Armor = 0.f; 
ExecutionParams.AttemptCalculateCapturedAttributeMagnitude(DamageStatics().ArmorDef, EvaluationParameters, Armor);

 

 이렇게 하여 캡쳐된 속성을 불러온다.

 

이제 이러한 속성들을 가져올 수는 있었는데 실제 우리가 원하는 작업(데미지를 입히는 작업)은 어떻게 해야할까?

 

매개변수를 보면 아웃파라메터가 있는 것을 볼 수 있다.

 

FGameplayModifierEvaluatedData

 

위의 구조체를 만들어서 어트리뷰트의 어떤 작업을 할지 정의하고 출력 파라메터의 모디파이어로 설정하면 된다.

 

const FGameplayModifierEvaluatedData EvaluedData(DamageStatics().ArmorProperty, EGameplayModOp::Additive, Armor);
OutExecutionOutput.AddOutputModifier(EvaluedData);

 

현재 작업은 크게 의미는 없다. 그저 아머를 바꾸는 작업을 할 뿐이다.

 

데미지 수치를 바꾸려면 데미지 값을 가져와 설정하면 된다. 이는 비교적 쉽다.

// SetByCallerMagnitude 부터 데미지 불러오기 
float Damage = Spec.GetSetByCallerMagnitude(FAuraGameplayTags::Get().Damage); 


const FGameplayModifierEvaluatedData EvaluedData(UAuraAttributeSet::GetIncomingDamageAttribute(), EGameplayModOp::Additive, Damage);
OutExecutionOutput.AddOutputModifier(EvaluedData);

 

게임플레이 이펙트에 들어가 우리가 만든 클래스를 할당해주면 된다.

 

이후에는 추가적으로 방어력을 적용한다던지 해서 데미지 수치를 조절할 수도 있을 것이다.