본문 바로가기

게임프로그래밍/실습2

[실습2] 32. 커스텀 계산식으로 어트리뷰트 설정하기

1. Modifier Magnitude Calculaitions

 

에디터에서 기본적으로 제공해주는 기능을 통해서 다양한 속성들을 원하는 계수들로 설정할 수 있지만 더 복잡한 식을 원할 수 있다. 그때 사용하는 것이 MMC 클래스이다.

 

MMC 클래스를 생성하도록 하자.

 

이제 이 클래스를 채우도록 하자.

 

class PRACTICE2_API UMMC_MaxHealth : public UGameplayModMagnitudeCalculation
{
    GENERATED_BODY()

public:
    UMMC_MaxHealth();

    virtual float CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const override;
};

 

게임플레이 이펙트 스펙에 접근이 가능한 함수 하나를 오버라이드 한다.

 

이 클래스는 어트리뷰트를 캡쳐하는 것 또한 가능하다.

 

private:
    FGameplayEffectAttributeCaptureDefinition VigorDef;

 

이제 위의 변수를 실제로 할당해주어야 할 것이다. 그리고 이 작업은 생성자에서 할 수 있다.

 

#include "AbilitySystem/AuraAttributeSet.h"

UMMC_MaxHealth::UMMC_MaxHealth()
{
    VigorDef.AttributeToCapture = UAuraAttributeSet::GetVigorAttribute();
}

 

엑세서를 만들었기 때문에 이렇게 접근이 가능하다.

 

이제 이 값을 타겟에서 캡쳐할지 소스에서 캡쳐할지를 정해야한다. 사실 이 경우에는 둘이 동일하긴 하지만 그래도 설정을 해놓자.

 

UMMC_MaxHealth::UMMC_MaxHealth()
{
    VigorDef.AttributeToCapture = UAuraAttributeSet::GetVigorAttribute();
    VigorDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target;
    VigorDef.bSnapshot = false;

    RelevantAttributesToCapture.Add(VigorDef);
}
정의를  설정한 다음 RelevantAttributesToCapture 배열에 저장해놓는다. 해당 배열은 MMC 클래스에서 사용할 속성들을 미리 등록하여 효율적으로 데이터를 처리하게 해주는 배열이다.
이제 속성을 저장했으니 계산을 진행할 수 있을 것이다.
 
float UMMC_MaxHealth::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
    const FGameplayTagContainer* SoueceTags = Spec.CapturedSourceTags.GetAggregatedTags();
    const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

    FAggregatorEvaluateParameters EvaluateParameters;
    EvaluateParameters.SourceTags = SoueceTags;
    EvaluateParameters.TargetTags = TargetTags;
}

 

태그를 가져와 FAggregatorEvaluateParameters 구조체에 저장해준다. 해당 구조체의 역할은 아래와 같다고 한다.


FAggregatorEvaluateParameters는 Unreal Engine의 **Gameplay Ability System (GAS)**에서 속성(Attribute)을 계산하거나 평가할 때 사용되는 구조체입니다. 속성 값을 평가하는 데 필요한 태그 정보상황별 설정을 담고 있으며, 계산 과정을 제어하는 역할을 합니다.


 

이제 아래와 같이 작성하자.

float Vigor = 0.f;
GetCapturedAttributeMagnitude(VigorDef, Spec, EvaluateParameters, Vigor);
Vigor = FMath::Max<float>(Vigor, 0.f);

 

GetCapturedAttributeMagnitude() 함수는 해당 정의의 어트리뷰트 속성의 magnitude를 3번째 매개변수에 할당해주는 함수이다.

 

첫번째 어트리뷰트 정의는 캡쳐가 되어있어야 하며 우리가 제일 처음에 한 작업이 캡쳐를 하는 작업이었다.

 

ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());
const int32 PlayerLevel = CombatInterface->GetPlayerLevel();

return  80.f + 2.5f * Vigor + 10.f * PlayerLevel;

 

그리고 이어서 작성한다. 캐릭터의 레벨을 가져왔고 기타 수학 계산을 통해 리턴하는 값이 바로 최대 체력의 값이 될 것이다. 이 클래스를 나중에 게임플레이 이펙트에 할당해주기만 하면 된다.

 


2. 커스텀 계산 어트리뷰트 이펙트에 할당하기

 

컴파일하고 에디터로 들어가자.

 

 

Magnitude Calculation Type 를 커스텀 클래스로 바꿔준다.

 

 

클래스를 선택해주면 된다. 항목들을 보면 알겠지만 커스텀 계산 후에도 추가로 또 계산을 진행할 수도 있다.

 

이제 게임을 키면..에러가 발생한다.

ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());

 

문제가 발생한 구문이다. 소스 오브젝트를 생성하지 않아 이 문제가 생겼다. 게임플레이 이펙트를 적용할때 소스 오브젝트도 알려주어야 한다.

 

베이스 캐릭터 클래스로 가자.

ApplyEffectToSelf

 

해당 함수에서 게임플레이 이펙트들을 적용하고 있다. 여기서 소스 객체를 할당해주자.

 

void AAuraCharacterBase::ApplyEffectToSelf(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level) const
{
    check(IsValid(GetAbilitySystemComponent()));
    check(GameplayEffectClass);
    
    FGameplayEffectContextHandle ContextHandle = GetAbilitySystemComponent()->MakeEffectContext();
    ContextHandle.AddSourceObject(this); // 추가
    const FGameplayEffectSpecHandle SpecHandle = GetAbilitySystemComponent()->MakeOutgoingSpec(GameplayEffectClass, Level, ContextHandle);
    GetAbilitySystemComponent()->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), GetAbilitySystemComponent());
}

 

컨텍스트 핸들에서 소스 오브젝트를 설정할 수 있다. 다시 컴파일하고 확인하자.

 

이제 정상적으로 작동한다. 이렇게 해서 더욱 정교하게 어트리뷰트를 구현할 수 있게 되었다.