UnrealEngine5/공부

[UE5/GAS] 데미지 전달에 GE 활용하기 (2) MetaAttribute와 CurveTable 활용하기

왹져박사 2025. 6. 2. 04:00

인프런 [이득우의 언리얼 프로그래밍 Part4 - 게임플레이 어빌리티 시스템] 강의 공부 노트

강의를 들으며 개념 정리 후, 다른 프로젝트에 실습하고 있습니다. 


 

1. MetaAttribute

Attribute 설정을 위해 사전에 설정하는 임시 Attribute

기획 추가에 유연한 대처 가능 → 데미지 기능에 대한 보정 기능들 처리

적용 후 바로 0으로 초기화하도록 설정

리플리케이션에서 제외하는 것이 일반적 (최종 변경 값만 전달하면 되기 때문)

 

 

1) AttributeSet에 MetaAttribute로 사용할 Damage Attribute 추가 후, 0으로 초기화

// Fill out your copyright notice in the Description page of Project Settings.


#include "Attribute/VSCharacterAttributeSet.h"
#include "GameplayEffectExtension.h"

#include "ProjectLOL.h"

UVSCharacterAttributeSet::UVSCharacterAttributeSet()
	: 
	AttackRange(100.f),
	MaxAttackRange(300.f),
	AttackRadius(50.f),
	MaxAttackRadius(150.f),
	AttackPower(30.f),
	MaxAttackPower(100.f),
	MaxHP(100.f),
	Damage(0.f)
{
	// Base HP, Current HP 모두 MaxHP로 초기화.
	InitHP(GetMaxHP());

	//LOL_LOG(LogProjectLOL, Log, TEXT("AttackRange : %f"), )
}

void UVSCharacterAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
	//// HP가 0 이하로 내려가면 보정. 
	//if (Attribute == GetHPAttribute())
	//{
	//	NewValue = FMath::Clamp(NewValue, 0, GetMaxHP());
	//}
	
	// Damage를 통해서 보정 값 설정하기.
	if (Attribute == GetDamageAttribute())
	{
		NewValue = NewValue < 0.f ? 0.f : NewValue;
	}

}

//void UVSCharacterAttributeSet::PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue)
//{
//	// HP 변경 값 디버깅.
//	if (Attribute == GetHPAttribute())
//	{
//		LOL_LOG(LogProjectLOL, Log, TEXT("Changed HP %f -> %f"), OldValue, NewValue);
//	}
//}

void UVSCharacterAttributeSet::PostGameplayEffectExecute(const FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);

	float MinHP = 0.f;

	if (Data.EvaluatedData.Attribute == GetHPAttribute())
	{
		LOL_LOG(LogProjectLOL, Warning, TEXT("Direct HP Access : %f"), GetHP());

		SetHP(FMath::Clamp(GetHP(), MinHP, GetMaxHP()));
	}
	else if (Data.EvaluatedData.Attribute == GetDamageAttribute())
	{
		LOL_LOG(LogProjectLOL, Log, TEXT("Damage : %f"), GetDamage());

		SetHP(FMath::Clamp(GetHP() - GetDamage(), MinHP, GetMaxHP()));

		SetDamage(0.f);
	}
}

 

2) GE를 DamageAttribute 기반으로 변경

 

결과 화면

 


2. 레벨에 따른 데미지 증가

레벨과 커브 테이블

GE에는 추가로 레벨 정보를 지정 가능

GE에 지정된 레벨 정보를 사용해 DataTable에서 특정 값을 가져오기 가능

ScalableFloat Modifier Type에서 사용 가능.

→ 활용하여 다양한 기능 구현 (현재 레벨에 따라 다른 초기 스탯 적용 ..)

CurveTable은 에디터로 제작

 

1) CurveTable 생성

 

선형 타입으로 생성  

 

행 추가하기

 

 

롤 집중포화의 나무위키를 참고하여 적용해 보자!

 

레벨에 따른 HP 설정 

레벨이 1부터 시작하니 최대 5 업그레이드인 6까지 0.1 배수씩 늘어나도록 설정하였다. 

 

2) GE에 적용

CurveTable값을 배수로 설정하여 Multifly 계산으로 설정하였다.

 

3) CharacterPlayer에 GE와 Level 추가

	UPROPERTY(EditAnywhere, Category = GAS)
	TSubclassOf<class UGameplayEffect> InitStatEffect;

	UPROPERTY(EditAnywhere, Category = GAS)
	float Level;

 

 

4) GA에서 GE 생성하기

GE 생성과정

GameplayEffectContext

  • GE에서 계산에 필요한 데이터를 담은 객체
  • Instigator(가해자), Causer(가해 수단), HitResult(판정 정보)

GameplayEffectSpec

  • Level, Modifier, Tag 등
  • GameplayEffectContextHandle

ASC는 각 데이터를 Handle 객체를 통해 간접적으로 관리

GameplayEffectContextHandle 생성 후 GameplayEffectSpecHandle 생성

 

 

4-1) CharacterPlayer에서 GE 생성하기

void AVSCharacterChampionPlayer::PossessedBy(AController* NewController)
{
	Super::PossessedBy(NewController);

	// PlayerState에서 ASC 가져오기. 
	AVSPlayerState* VSPlayerState = GetPlayerState<AVSPlayerState>();
	if (VSPlayerState)
	{
		ASC = VSPlayerState->GetAbilitySystemComponent();
		//...

		// GE 생성.
		FGameplayEffectContextHandle EffectContextHandle = ASC->MakeEffectContext();
		EffectContextHandle.AddSourceObject(this);
		FGameplayEffectSpecHandle EffectSpecHandle = ASC->MakeOutgoingSpec(InitStatEffect, Level, EffectContextHandle);
		if (EffectSpecHandle.IsValid())
		{
			ASC->BP_ApplyGameplayEffectSpecToSelf(EffectSpecHandle);
		}
	}
}

 

4-2) CharacterPlayer의 Blueprint에서 GE 지정

 


결과 화면

1, 3, 6 Level HP 변경된 값 

 

 


이제 이 Level도 시스템 상 데이터로 변경해 주는 작업을 해야 할 듯하다.