언리얼/게임 프로젝트

GameplayAbilitySystem을 이용한 RPG 프로젝트 - (37) Custom Calculation 클래스와 Vital 스탯 초기화

monstro 2025. 2. 11. 00:47
728x90
반응형

이번 포스트에서는 이전에 설정한 Gameplay Effect

사용자가 정의하는 계산 방식Custom Calculation 클래스사용해보도록 하겠습니다.

총 2개의 Attribute인 MaxHealth와 MaxMana를 위의 방식으로 설정해보도록 하겠습니다.

또한 이전에 하드코딩으로 설정하던 Health와 Mana를 GE를 통해 설정하는 것도 진행하겠습니다.

 

1) MMC_MaxHealth

#pragma once

#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "MMC_MaxHealth.generated.h"

/**
 * 
 */
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;
};

 

include "ModMagCalc/MMC_MaxHealth.h"
#include "AbilitySystem/AuraAttributeSet.h"
#include "Interface/CombatInterface.h"

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

	RelevantAttributesToCapture.Add(VigorDef);
}

float UMMC_MaxHealth::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
	// Gather tags from source and target
	const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;

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

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

	// Return value of MaxHealth
	return 80.f + 2.5f * Vigor + 10.f * PlayerLevel;
}

 

사용자가 설계하는 계산 방식 클래스

GameplayModMagnitudeCalculation에서 상속받은 MMC_MaxHealth 클래스정의하였습니다.

 

1 - 1) 생성자

생성자에서는 

GameplayEffectAttributeCaptureDefinition 자료형으로 선언된 프로퍼티를 설정하고 있습니다.

GameplayEffectAttributeCaptureDefinition 자료형프로퍼티는 

GameplayEffect 내에서 캡쳐중인 Attribute를 저장합니다.

캡쳐를 하게 되면 GE가 Attribute를 수정하는 경우 캡쳐한 Attribute를 기준으로 수정하게 됩니다.

 

또한, 캡쳐하는 Attribute의 주인현재 GameplayEffect의 Target으로 설정하였습니다.

따라서 GE가 Attribute를 설정하는 대상Attribute를 기반으로 계산을 수행합니다.

 

마지막으로 Snapshot을 설정하는 것을 볼 수 있는데,

스냅샷을 끄게 되면 즉, 스냅샷을 False로 설정하면 GE가 적용된 후 Attribute를 변화시킵니다.
만약 스냅샷을 키게 되면 즉, 스냅샷을 True로 설정하면 GE가 생성된 후 Attribute를 변화시킵니다. 

 

최종적으로 RelevantAttributesToCapture 메서드를 통해 

GE의 CustomCalc인자로 넣어준 Attribute기반으로 동작하도록 설정합니다.

 

1 - 2) CalculateBaseMagnitude_Implementation

GameplayModMagnitudeCalculation 클래스에서 지원하는 메소드 중 하나인 

CalculateBaseMagnitude_Implementation의 경우, Custom Calculation이 동작하는 방식을 정의합니다.

위의 로직을 보면 GameplayEffectSource와 Target으로부터 Tag를 가져온 후

FAggregatorEvaluateParameters완성합니다.

 

완성된 FAggregatorEvaluateParameters로부터 계산의 기반으로 삼을 Attribute 값을 설정합니다.

예제에서는 Vigor와 Level의 값을 설정합니다.

 

최종적으로 계산식에 따라 완성된 값을 리턴합니다.

 

2) MMC_MaxMana

#pragma once

#include "CoreMinimal.h"
#include "GameplayModMagnitudeCalculation.h"
#include "MMC_MaxMana.generated.h"

/**
 * 
 */
UCLASS()
class AURA_API UMMC_MaxMana : public UGameplayModMagnitudeCalculation
{
	GENERATED_BODY()
	
public:
	UMMC_MaxMana();

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

private:
	FGameplayEffectAttributeCaptureDefinition IntDef;
};

 

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

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

	RelevantAttributesToCapture.Add(IntDef);
}

float UMMC_MaxMana::CalculateBaseMagnitude_Implementation(const FGameplayEffectSpec& Spec) const
{
	// Gather tags from source and target
	const FGameplayTagContainer* SourceTags = Spec.CapturedSourceTags.GetAggregatedTags();
	const FGameplayTagContainer* TargetTags = Spec.CapturedTargetTags.GetAggregatedTags();

	FAggregatorEvaluateParameters EvaluationParameters;
	EvaluationParameters.SourceTags = SourceTags;
	EvaluationParameters.TargetTags = TargetTags;

	// Int 설정
	float Int = 0.f;
	GetCapturedAttributeMagnitude(IntDef, Spec, EvaluationParameters, Int);
	Int = FMath::Max<float>(Int, 0.f);

	// 레벨 설정
	ICombatInterface* CombatInterface = Cast<ICombatInterface>(Spec.GetContext().GetSourceObject());
	const int32 PlayerLevel = CombatInterface->GetPlayerLevel();

	// 최종 MaxMana 설정
	return 50.f + 2.5f * Int + 15.f * PlayerLevel;
}

 

MMC_MaxMana의 경우, MMC_MaxHealth와 그리 큰 차이가 없으므로 설명은 생략합니다.

 

3) AuraCharacterBase

.
.
.

UCLASS(Abstract)
class AURA_API AAuraCharacterBase : public ACharacter, public IAbilitySystemInterface, public ICombatInterface
{
	GENERATED_BODY()

	.
	.
	.

protected:
	
	.
	.
	.

	/*
	* GAS Section
	*/
	.
	.
	.

	UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Attributes")
	TSubclassOf<UGameplayEffect> DefaultVitalAttributes;

	.
	.
	.

};

 

.
.
.

void AAuraCharacterBase::InitializeDefaultAttributes() const
{
	
	.
	.
	.
    
	ApplyEffectToSelf(DefaultVitalAttributes, 1.f);
}

 

Health와 Mana 역시 SecondaryAttribute가 설정된 후, 

GameplayEffect로 설정할 수 있도록 위와 같이 로직을 수정하겠습니다.

 

4) 언리얼 에디터

4 - 1) SecondaryAttribute를 설정하는 GameplayEffect 블루프린트

 

 

4 - 3) Vital Attribute를 설정하는 GameplayEffect 블루프린트

 

 

4 - 4) 캐릭터 블루 프린트

 

최종 실행 결과는 다음과 같습니다.

728x90
반응형