Udemy Unreal - Gameplay Ability System - Top Down RPG (Section 5)

๊น€์ข…๋ฏผยท2025๋…„ 4์›” 7์ผ

Udemy Unreal - GAS

๋ชฉ๋ก ๋ณด๊ธฐ
5/7

๐Ÿ“– ์œ ๋ฐ๋ฏธ GAS ๊ฐ•์˜ ์ค‘ ๊ธฐ๋กํ•ด๋‘๊ณ  ์‹ถ์€ ๋‚ด์šฉ์„ ์ ์–ด๋‘” ํฌ์ŠคํŠธ

Section 5 : Gameplay Effect


GameplayEffect

UObject ํด๋ž˜์Šค๋ฅผ ์ƒ์†๋ฐ›์€ ๋ฐ์ดํ„ฐ ํด๋ž˜์Šค์ด๋‹ค.

  • ์‚ฌ์šฉํ•  ๋•Œ๋Š” ๋”ฐ๋กœ ์ž์‹ ํด๋ž˜์Šค ๋งŒ๋“ค์ง€ ๋ง๊ณ  ์ด ํด๋ž˜์Šค๋กœ BP๋ฅผ ๋งŒ๋“ค๋ฉด ๋œ๋‹ค.
    (์ด๋ฏธ ์ถฉ๋ถ„ํžˆ ๋งŽ์€ ๊ธฐ๋Šฅ์ด ์žˆ๊ณ  ๋‹ค์–‘ํ•œ ๋กœ์ง์ด ๊ตฌ์„ฑ๋˜์–ด ์žˆ์Œ)

Attribute์™€ GameplayTag๋ฅผ ์ˆ˜์ •ํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

Modifier : ๊ฐ’ ์ˆ˜์ •
Execution : ๊ฐ’ ์ˆ˜์ •๋ฟ ์•„๋‹ˆ๊ณ  ์ฝ”๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์„œ ํŠน์ • ์•ก์…˜ ์ˆ˜ํ–‰ ๊ฐ€๋Šฅ


GameplayEffectSpec

GameplayEffect

์ •์  ์ •์˜
์†์„ฑ ๋ณ€๊ฒฝ, ์ง€์† ์‹œ๊ฐ„, ์ฃผ๊ธฐ, ํƒœ๊ทธ, ๊ทธ๋ฆฌ๊ณ  ์กฐ๊ฑด๋ถ€ ๋…ผ๋ฆฌ ๋“ฑ์„ ์„ค์ •ํ•ด ๋†“์€ ์„ค๊ณ„๋„
ํŠน์ˆ˜ํ•œ ์ƒํ™ฉ ๊ตฌํ˜„์„ ์œ„ํ•ด ์„œ๋ธŒํด๋ž˜์Šค๋ฅผ ๋งŒ๋“œ๋Š” ๋Œ€์‹  Exectuion์ด๋‚˜ Modifier๋ฅผ ํ™œ์šฉํ•˜๊ธฐ

GameplayEffectSpec

๋Ÿฐํƒ€์ž„์— ์ƒ์„ฑ๋˜๋Š” ์ธ์Šคํ„ด์Šค
FGameplayEffectSpec ๊ตฌ์กฐ์ฒด๋กœ ์กด์žฌ
ํŠน์ • GameplayEffect๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ตฌ์ฒด์ ์ธ ์ˆ˜์น˜, ์ ์šฉ ๋Œ€์ƒ, ๋ ˆ๋ฒจ ๋“ฑ์„ ํฌํ•จํ•ด์„œ ๋งŒ๋“ค์–ด์ง

๊ด€๊ณ„

  1. GameplayEffect๋ฅผ ๋งŒ๋“ ๋‹ค.

  2. ๊ฒŒ์ž„ ์ค‘ ๋Šฅ๋ ฅ์ด ๋ฐœ๋™๋˜๋ฉด ์ฝ”๋“œ์—์„œ MakeOutgoingSpec() ๊ฐ™์€ ํ•จ์ˆ˜๋ฅผ ์จ์„œ GameplayEffectSpec์„ ์ƒ์„ฑํ•œ๋‹ค.

  3. ์ด GameplayEffectSpec์ด ApplyGameplayEffectSpecToTarget() ๋“ฑ์˜ ํ•จ์ˆ˜๋กœ ํƒ€๊ฒŸ์—๊ฒŒ ์ ์šฉ๋œ๋‹ค.

์ ์šฉ ์˜ˆ์‹œ

void AAuraEffectActor::ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass)
{
	// UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent ์‚ฌ์šฉํ•˜๋ฉด ASC ๋ถˆ๋Ÿฌ์˜ฌ ์ˆ˜ ์žˆ์Œ
	// ๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด
	// 1. IAbilitySystemInterface ์ƒ์† ๋ฐ›์€ ๊ฒฝ์šฐ : ์ธํ„ฐํŽ˜์ด์Šค ๊ตฌํ˜„ ํ•จ์ˆ˜๋กœ ๋ถˆ๋Ÿฌ์˜ด
	// 2. ๋ฐ›์ง€ ์•Š์€ ๊ฒฝ์šฐ : AbilitySystemComponent๋ฅผ ์ฐพ์•„์„œ ๋ถˆ๋Ÿฌ์˜ค๋Š” 2๊ฐ€์ง€ ๊ฒฝ์šฐ ๋ชจ๋‘ ๋Œ€์‘ํ•จ
	UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
	if (TargetASC == nullptr)
	{
		return;
	}

	check(GameplayEffectClass);
	// FGameplayEffectContextHandle ๋งŒ๋“ค๊ธฐ
	FGameplayEffectContextHandle EffectContextHandle = TargetASC->MakeEffectContext();
	// Context์—๋Š” ๋‹ค์–‘ํ•œ ์ •๋ณด๊ฐ€ ๋“ค์–ด๊ฐ€๊ณ  Handle์€ Wrapper ํด๋ž˜์Šค์ž„
	// EffectContext์— ์ •๋ณด ์ถ”๊ฐ€ (์ด ์ดํŽ™ํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚จ ๊ฐ์ฒด ์ถ”๊ฐ€ํ•œ ๊ฒƒ)
	EffectContextHandle.AddSourceObject(this);
	
	// FGameplayEffectSpec ๋งŒ๋“ค๊ธฐ
	FGameplayEffectSpecHandle EffectSpecHandle = TargetASC->MakeOutgoingSpec(GameplayEffectClass,ActorLevel,EffectContextHandle);

	// ApplyGameplayEffect~~ ์ข…๋ฅ˜ ์—ฌ๋Ÿฌ๊ฐ€์ง€ ์žˆ์Œ
	// ์ธ์ž๋กœ EffectSpec ์ฐธ์กฐ ํฌ์ธํ„ฐ๊ฐ€ ํ•„์š”ํ•œ๋ฐ EffectSpecHandle์€ ๋ž˜ํผ์ด๊ณ  ๊ทธ ์•ˆ์—๋Š” Data(SharedPtr)๊ฐ€ ๋“ค์–ด์žˆ์Œ
	// SharedPtr ์ž์ฒด๋„ ๋ž˜ํผ์ด๋ฏ€๋กœ Getํ•˜๋ฉด ํฌ์ธํ„ฐ๊ฐ€ ๋‚˜์˜ด
	// ํฌ์ธํ„ฐ๊ฐ€ ์•„๋‹Œ ๊ทธ ์ฐธ์กฐ๊ฐ’ ์ž์ฒด๊ฐ€ ํ•„์š”ํ•˜๋ฏ€๋กœ * ๋ฅผ ๋ถ™์—ฌ์คŒ
	FActiveGameplayEffectHandle ActiveGameplayEffectHandle = TargetASC->ApplyGameplayEffectSpecToSelf(*EffectSpecHandle.Data.Get());
}

ApplyGameplayEffectSpecToTarget()์€ FActiveGameplayEffectHandle์„ ๋ฆฌํ„ดํ•œ๋‹ค.

  • ํ•ด๋‹น ํ•ธ๋“ค์„ ํ†ตํ•ด ์ดํŽ™ํŠธ๋ฅผ ์ปจํŠธ๋กค ํ•  ์ˆ˜ ์žˆ์Œ
    ex) ๊ธฐ์กด ์ ์šฉ๋œ ์ดํŽ™ํŠธ๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ

Duration Policy

GameplayEffect์˜ DurationPolicy๋Š” ํฌ๊ฒŒ ์„ธ๊ฐ€์ง€

Instant

AttributeData์˜ BaseValue๋ฅผ ์ฆ‰๊ฐ์ ์œผ๋กœ ์ˆ˜์ •

Duration

AttributeData์˜ CurrentValue๋ฅผ ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ์ˆ˜์ •
-> ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด BaseValue๋กœ ๋Œ์•„์˜จ๋‹ค.

ex) 5์ดˆ๊ฐ„ ๋ฐฉ์–ด๋ ฅ์ด 100 ์ฆ๊ฐ€ํ•˜๋Š” ๊ฒฝ์šฐ (๊ธฐ์กด 10)

  • 5์ดˆ๊ฐ„ 110 ์œ ์ง€ ํ›„ 10์œผ๋กœ ๋Œ์•„์˜จ๋‹ค.

Inifinite

AttributeData์˜ CurrentValue๋ฅผ ํŠน์ • ์‹œ๊ฐ„๋™์•ˆ ์ˆ˜์ •
-> ์‹œ๊ฐ„์ด ์ง€๋‚˜๋ฉด BaseValue๋กœ ๋Œ์•„์˜จ๋‹ค.

ex) ๋ฐฉ์–ด๋ ฅ์ด 100 ์ฆ๊ฐ€ํ•˜๋Š” ๊ฒฝ์šฐ (๊ธฐ์กด 10)

  • ํ•ด์ œ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” 110 ์œ ์ง€ํ•˜๋‹ค๊ฐ€ ์ดํŽ™ํŠธ ๋น„ํ™œ์„ฑํ™”ํ•˜๋ฉด 10์œผ๋กœ ๋Œ์•„์˜จ๋‹ค.
    (FActiveGameplayEffectHandle ํ™œ์šฉ)
// Remove ์ธ์ž์— -1 (๊ธฐ๋ณธ๊ฐ’) ๋„ฃ์œผ๋ฉด ๋ชจ๋“  ์Šคํƒ์ด ์ œ๊ฑฐ๋จ
ASC->RemoveActiveGameplayEffect(ActiveGameplayEffectHandle,1); // 1๊ฐœ๋งŒ ์ œ๊ฑฐ

Periodic

Duration, Infinite์—๋งŒ ์ ์šฉ

ํŠน์ • ๊ธฐ๊ฐ„๋™์•ˆ ์ ์šฉ๋˜๋Š” ์ดํŽ™ํŠธ์— n์ดˆ๋งˆ๋‹ค ์ดํŽ™ํŠธ๊ฐ€ ๋ฐœ๋™ํ•˜๋„๋ก ํ•ด์ค€๋‹ค.
BaseValue์— ์ง์ ‘ ์˜ํ–ฅ์„ ์ค€๋‹ค.

  • CurrentValue ์ˆ˜์ • ํ›„ ๋Œ์•„์˜ค๋Š” ๋ฐฉ์‹์ด ์•„๋‹ˆ๊ธฐ๋•Œ๋ฌธ์— ์ดํŽ™ํŠธ๊ฐ€ ํ’€๋ ค๋„ ๊ธฐ์กด๊ฐ’์œผ๋กœ ๋Œ์•„์˜ค์ง€ ์•Š์Œ

ex) 2์ดˆ๋™์•ˆ ์ด 20์˜ ์ฒด๋ ฅ ํšŒ๋ณต์„ ํ•˜๋Š” ๊ฒฝ์šฐ (Period๋Š” 0.1์ดˆ ๊ฐ„๊ฒฉ)

  • Duration : 2์ดˆ
  • Period : 0.1
  • Modifier : Add , 1

EffectStack

ํ•œ๋ฒˆ์— ์ ์šฉ๋˜๋Š” ์ดํŽ™ํŠธ์˜ ์ˆ˜๋ฅผ ์ œํ•œํ•  ์ˆ˜ ์žˆ๋‹ค.

Stacking Type

bySource : ๊ฐ™์€ ํƒ€๊ฒŸ์ด์–ด๋„ ์†Œ์Šค๊ฐ€ ๋‹ค๋ฅด๋ฉด ์ดํŽ™ํŠธ ์Šคํƒ ๋”ฐ๋กœ ๊ณ„์‚ฐ

byTarget : ๊ฐ™์€ ํƒ€๊ฒŸ์ด๋ผ๋ฉด ์†Œ์Šค๊ฐ€ ๋‹ฌ๋ผ๋„ ์Šคํƒ์„ ํ•ฉ์ณ์„œ ๊ณ„์‚ฐ


์ดํŽ™ํŠธ ์ ์šฉ ์ „,ํ›„ ์ฒ˜๋ฆฌ

PreAttributeChange()

์ดํŽ™ํŠธ ์ ์šฉ ์ „ ๊ฐ’ ํด๋žจํ•‘ํ•  ๋•Œ ์‚ฌ์šฉ (๋‹ค๋ฅธ ํ•จ์ˆ˜ ํ˜ธ์ถœ์ด๋‚˜ ๊ธฐ๋Šฅ์€ ํ•˜์ง€ ์•Š์„ ๊ฒƒ ๊ถŒ์žฅ)

์˜ค๋ฒ„๋ผ์ด๋“œํ•ด์„œ ์‚ฌ์šฉ

virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) override;
.
.
.
void UAuraAttributeSet::PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue)
{
	Super::PreAttributeChange(Attribute, NewValue);
	
    // Accessor ๋งคํฌ๋กœ ์‚ฌ์šฉํ•ด์„œ ์›ํ•˜๋Š” Attribute์— ์ ์šฉ
	if (Attribute == GetHealthAttribute())
	{
    	// NewValue๋ฅผ ์ˆ˜์ •
		NewValue =  FMath::Clamp(NewValue, 0.f , GetMaxHealth());
	}
}

์ดํŽ™ํŠธ ์ ์šฉ ์ „์— ํ˜ธ์ถœ๋˜๋Š” ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ด ์ดํ›„ ๋‹จ๊ณ„์—์„œ ๊ฐ’ ์ˆ˜์ •์ด ์ผ์–ด๋‚œ๋‹ค๋ฉด
์ตœ์ข…์ ์œผ๋กœ๋Š” ํด๋žจํ•‘ ๋ฒ”์œ„๋ฅผ ๋„˜์–ด๊ฐˆ ์ˆ˜๋„ ์žˆ์œผ๋ฏ€๋กœ ์ฃผ์˜ ํ•„์š”.


PostGameplayEffectExecute()

๊ฒŒ์ž„ํ”Œ๋ ˆ์ด ์ดํŽ™ํŠธ ์‹คํ–‰ ํ›„ ํ˜ธ์ถœ๋˜๋Š” ํ•จ์ˆ˜๋กœ Data๋ฅผ ํ†ตํ•ด ์—ฐ๊ด€๋œ ๋Œ€๋ถ€๋ถ„์˜ ์—”ํ‹ฐํ‹ฐ์— ์ ‘๊ทผ ๊ฐ€๋Šฅํ•จ

์˜ค๋ฒ„๋ผ์ด๋“œํ•ด์„œ ์‚ฌ์šฉ

virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) override;
.
.
.
void UAuraAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)
{
	Super::PostGameplayEffectExecute(Data);

	// EffectContextHandle
	EffectContextHandle = Data.EffectSpec.GetContext();
    // Source์˜ AbilitySystemComponent ๋ฐ›์•„์˜ค๊ธฐ
	SourceASC = Props.EffectContextHandle.GetOriginalInstigatorAbilitySystemComponent();
	// SourceASC๋ฅผ ํ†ตํ•ด AvatarActor, Controller ๋“ฑ์— ์ ‘๊ทผ ๊ฐ€๋Šฅ
	
	// Target์˜ ์•กํ„ฐ ์ •๋ณด์—๋„ ์ ‘๊ทผ ๊ฐ€๋Šฅ
	if (Data.Target.AbilityActorInfo.IsValid() && Data.Target.AbilityActorInfo->AvatarActor.IsValid())
	{
    	// Target์˜ AvatarActor์™€, ASC๋“ฑ์— ์ ‘๊ทผ
		TargetAvatarActor = Data.Target.AbilityActorInfo->AvatarActor.Get();
		TargetController = Data.Target.AbilityActorInfo->PlayerController.Get();
		TargetCharacter = Cast<ACharacter>(Props.TargetAvatarActor);
		TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(Props.TargetAvatarActor);
	}
	
}

FGameplayEffectModCallbackData๋ฅผ ํ†ตํ•ด ๋‹ค์–‘ํ•œ ์ •๋ณด์— ์ ‘๊ทผํ•˜๊ณ  ์›ํ•˜๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค.


CurveTable

GameplayEffect BP์—์„œ ๋ ˆ๋ฒจ์— ๋”ฐ๋ฅธ Magnitude ๊ฐ’ ์ ์šฉ์„ ์œ„ํ•ด ์ปค๋ธŒํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.


float ๊ฐ’ * ์ปค๋ธŒ ๋‚ด์˜ level์— ํ• ๋‹น๋œ ๊ฐ’์ด ๊ณฑํ•ด์ ธ์„œ ์ ์šฉ๋จ
ex) 1 * HealingCurve Level 1 ๊ฐ’ 5.f ์ธ 5๊ฐ€ ์ ์šฉ๋จ (Preview At 1 : 5.000)

GameplayEffectSpec์„ ๋งŒ๋“ค ๋•Œ ๋ ˆ๋ฒจ ๋„ฃ์–ด์คŒ

// FGameplayEffectSpec ๋งŒ๋“ค๊ธฐ
TargetASC->MakeOutgoingSpec(GameplayEffectClass,ActorLevel,EffectContextHandle);

์ปค๋ฐ‹

https://github.com/jongmin7759/Unreal_GAS/commit/f3b3cfe6533cc40d1eec12ff70f2d76a282be4f3
https://github.com/jongmin7759/Unreal_GAS/commit/69c7bbe7bd815cefe911d6076443df6f378537d1

profile
์ •๋ฆฌ์™€ ๊ธฐ๋ก

0๊ฐœ์˜ ๋Œ“๊ธ€