계기
SMASkillProjectile에서 투사체가 적에게 맞았을 때 데미지를 적용하는 코드를 작성하다가 두 핸들의 역할이 헷갈렸다.
FGameplayEffectContextHandle ContextHandle = SourceASC->MakeEffectContext();
ContextHandle.AddInstigator(Avatar, Controller);
FGameplayEffectSpecHandle SpecHandle = SourceASC->MakeOutgoingSpec(DamageEffectClass, 1.f, ContextHandle);
SpecHandle.Data->SetSetByCallerMagnitude(SMSkillTag::Data_Damage_Amount, -BaseDamage);
TargetASC->ApplyGameplayEffectSpecToSelf(*SpecHandle.Data.Get());
왜 두 개를 따로 만들어야 하는지, 그냥 하나로 합칠 수 없는지 의문이 생겼다.
GameplayEffectContextHandle — 출처 증명서
"누가, 어떤 상황에서 이 GE를 발동했는가" 에 대한 메타데이터를 담는 구조체다.
FGameplayEffectContextHandle Context = ASC->MakeEffectContext();
Context.AddInstigator(Avatar, Controller); // 시전자 + 컨트롤러
Context.AddOrigin(GetActorLocation()); // 발동 위치
Context.AddHitResult(HitResult); // 피격 정보
GE가 적용될 때 부가 정보를 함께 전달하는 봉투 역할을 한다. 이 정보는 이후 아래와 같은 곳에서 꺼내 쓴다.
- 데미지 계산 시 Instigator 귀속
- GameplayCue의 피격 위치/방향
- 어트리뷰트 변경 이벤트에서 누가 때렸는지 추적
GameplayEffectSpecHandle — 실행 계획서
"어떤 GE를, 어떤 수치로 적용할 것인가" 에 대한 실행 설계도다.
FGameplayEffectSpecHandle Spec = ASC->MakeOutgoingSpec(DamageEffectClass, Level, Context);
Spec.Data->SetSetByCallerMagnitude(Tag, -100.f); // 수치 런타임 주입
Spec.Data->DynamicGrantedTags.AddTag(CooldownTag); // 태그 런타임 추가
ContextHandle을 내부에 포함하면서, 실제로 적용될 GE의 클래스, 레벨, 수치, 태그 등 모든 정보를 담고 있다.
둘의 관계
ContextHandle → SpecHandle → ApplyGameplayEffectSpecToSelf
(누가 / 어디서 / 어떻게) (무엇을 / 얼마나 / 어떤 태그) (실제 적용)
SpecHandle이 ContextHandle을 포함하는 구조다. ContextHandle 없이 Spec을 만드는 것도 가능하지만, 그 경우 GE는 적용되어도 시전자 정보가 없어서 Instigator가 None으로 남는다.
핵심 정리
ContextHandle SpecHandle
| 역할 | 출처 증명서 | 실행 계획서 |
| 담는 것 | 시전자, 위치, 피격 정보 | GE 클래스, 수치, 태그 |
| 포함 관계 | — | ContextHandle을 내부에 포함 |
| 없으면 | Instigator 추적 불가 | GE 적용 자체가 불가 |
ContextHandle은 "누가 쐈는지", SpecHandle은 "무엇을 얼마나 쐈는지"다.
출처 없이 계획만 있으면 GE는 적용되지만, 누가 쐈는지 게임이 모른다.
'프로그래밍 > Unreal Engine 5' 카테고리의 다른 글
| [Unreal Engine5] GA_SkillBase 리팩토링 회고 — 애니메이션 추가와 GameplayCue owner 복제 이슈 (1) | 2026.04.21 |
|---|---|
| [Unreal Engine 5] GAS_멀티플레이에서 GameplayCue가 작동하는 방식 (1) | 2026.04.17 |
| [Unreal Engine 5] 멀티플레이 아키텍처: GAS 퀵슬롯과 인벤토리 설계 (0) | 2026.04.09 |
| [Unreal Engine 5] 멀티플레이어 디버깅 유틸리티 & WorldContext (0) | 2026.04.06 |
| [Unreal Engine 5] Module과 Plugin의 차이점 (0) | 2026.04.02 |