0. 개요

이전 포스트에서 MMC의 정의를 학습하고 ASC(Ability System Component)에 레벨을 도입했다.

 

6-5. 커스텀 수정자 크기 계산(MMC ; Modifier Magnitude Calculation) - (1) 정의, 레벨 개념 추가

0. 개요특정 클래스에 종속적이지 않은 계산 방법을 도입하려 한다. 1. MMC(Modifier Magnitude Calculation)란?기존에 최대 체력을 계산할 때 Vigor 뿐 아니라 레벨의 10배를 곱한 것을 추가하려 한다.(이 프

crat.tistory.com

이제 MMC를 생성하고 게임플레이 이펙트에 적용하여 최대 체력과 최대 마나를 각각의 속성과 Attribute가 아닌 레벨을 가져와 커스텀된 계산 클래스를 생성한다.

 

 

1. MMC

(1) 클래스 생성

MMC를 C++ 클래스로 생성한다.

MaxHealth를 활력 속성과 레벨에 따라 증가시키도록 할 것이다.

 

 

(2) 코드

UCLASS()
class AURA_API UMMC_MaxHealth : public UGameplayModMagnitudeCalculation
{
	GENERATED_BODY()
	
public:
	UMMC_MaxHealth();

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

private:
	// 게임플레이 이펙트 속성 캡쳐
	FGameplayEffectAttributeCaptureDefinition VigorDef;
};

MMC를 정의하는 함수인 CalculateBaseMagnitude_Implementation을 오버라이딩한다.

게임플레이 이펙트에서 활력 속성을 캡쳐하기 위해 FGameplayEffectAttributeCaptureDefinition 구조체 타입을 정의한다.

UMMC_MaxHealth::UMMC_MaxHealth()
{
	// vigor 속성 캡쳐하기
	VigorDef.AttributeToCapture = UAuraAttributeSet::GetVigorAttribute();

	// 소스와 타겟이 동일(AuraCharacter)
	VigorDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target;

	// 첫 시점의 속성을 캡쳐할 것인가, 적용 순간의 속성을 캡쳐할 것인가
	VigorDef.bSnapshot = false;

	// 캡쳐할 속성들을 Array에 추가
	RelevantAttributesToCapture.Add(VigorDef);
}

생성자에서는 게임플레이 이펙트의 Backing Attribute(기반 속성)를 정의한다.

추가할 속성을 RelevantAttributesToCapture Array에 추가한다.

 

float UMMC_MaxHealth::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
	// 소스와 대상의 태그 수집
	const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

	// 속성의 값 가져오기
	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;

	// non-const
	float Vigor = 0.f;
	GetCapturedAttributeMagnitude(VigorDef, Spec, EvaluationParameters, Vigor);
	// Vigor는 타겟의 활력값을 가지게됨

	// 활력 양수 클램핑
	Vigor = FMath::Max<float>(Vigor, 0.f);

	// 레벨에 따라 증가
	ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());
	const int32 PlayerLevel = CombatInterface->GetPlayerLevel();

	// 공식
	return 80.f + Vigor * 2.5f + 10.f * PlayerLevel;
}

캡쳐한 속성의 크기(Magnitude)를 가져오기 위해 게임플레이 이펙트 스펙, 속성, 파라미터, 저장할 변수를 입력한다.

GetCapturedAttributeMagnitude 함수를 통해 Vigor가 타겟의 활력 속성 값을 저장하게 된다.

 

최종적으로 플레이어 캐릭터(UAuraCharacter)를 인터페이스(ICombatInterface)로 캐스팅하여 플레이어 레벨을 가져온 뒤에 공식을 적용하여 리턴하면 된다.


- FAggregatorEvaluateParameters


 

 

(3) 게임플레이 이펙트에 MMC 적용하기

 

 

2. 버그 수정

이제 디버그를 실행하면 크래시가 발생할 것이다.

CombatInterface가 nullptr로 표시되는데 그 이유는 Spec의 SourceObject가 nullptr이기 때문이다.

따라서 AuraCharacterBase로 돌아가서 컨텍스트 핸들에 본인을 소스로 지정하도록 한다.

void AAuraCharacterBase::ApplyEffectToSelf(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level) const
{
	check(IsValid(GetAbilitySystemComponent()));
	check(GameplayEffectClass);

	// 컨텍스트 핸들
	FGameplayEffectContextHandle ContextHandle = GetAbilitySystemComponent()->MakeEffectContext();
	
	// 컨텍스트 핸들에 본인을 소스로 지정 <- 이펙트를 본인에게 적용하기 때문
	ContextHandle.AddSourceObject(this);

	// 스펙 핸들
	FGameplayEffectSpecHandle SpecHandle = GetAbilitySystemComponent()->MakeOutgoingSpec(GameplayEffectClass, Level, ContextHandle);
	
	// 게임플레이 이펙트를 타겟에게 적용함
	GetAbilitySystemComponent()->ApplyGameplayEffectSpecToTarget(*SpecHandle.Data.Get(), GetAbilitySystemComponent());
}

 

 

속성이 아닌 값을 속성에 적용하거나, 두 개 이상의 속성을 복합적으로 적용하려고 할 때 MMC를 사용하는 것이 효과적임을 알 수 있다.

 

 

3. 최대 마나에 대해 MMC 적용

UCLASS()
class AURA_API UMMC_MaxMana : public UGameplayModMagnitudeCalculation
{
	GENERATED_BODY()

public:
	UMMC_MaxMana();

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

private:
	FGameplayEffectAttributeCaptureDefinition IntelligenceDef;
};
// Fill out your copyright notice in the Description page of Project Settings.


#include "AbilitySystem/ModMagCalc/MMC_MaxMana.h"
#include "AbilitySystem/AuraAttributeSet.h"
#include "Interaction/CombatInterface.h"

UMMC_MaxMana::UMMC_MaxMana()
{
	IntelligenceDef.AttributeToCapture = UAuraAttributeSet::GetIntelligenceAttribute();
	IntelligenceDef.AttributeSource = EGameplayEffectAttributeCaptureSource::Target;
	IntelligenceDef.bSnapshot = false;

	RelevantAttributesToCapture.Add(IntelligenceDef);
}

float UMMC_MaxMana::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
	// 태그
	const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

	//
	FAggregatorEvaluateParameters EvaluateParameters;
	EvaluateParameters.SourceTags = SourceTags;
	EvaluateParameters.TargetTags = TargetTags;

	// 속성 캡쳐
	float Intelligence = 0.f;
	GetCapturedAttributeMagnitude(IntelligenceDef, Spec, EvaluateParameters, Intelligence);

	// 속성 클램핑
	Intelligence = FMath::Max(Intelligence, 0.f);

	// 레벨 가져오기
	ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().Get()->GetSourceObject());
	int32 PlayerLevel = CombatInterface->GetPlayerLevel();

	return 50 + 2.5f * Intelligence + 15 * PlayerLevel;

}