Element Gauge - GameplayEvent 기반 충전 시스템
한 줄 요약
플레이어가 발생시킨 GameplayEvent(HitSuccess, Parry.Success 등)를 ASC가 받으면, DataTable 기반 룰에 따라 ElementGaugeComponent가 정해진 양만큼 게이지를 충전한다.
왜 DataTable인가
처음엔 컴포넌트 멤버 TMap<FGameplayTag, int32>로 충분하다고 봤지만, 수치 튜닝 빈도가 높을 것으로 예상되어 DataTable로 승격. 향후 행에 추가 컨텍스트(VFX, 사운드, 약점 보너스 배율 등)를 묶기에도 유리.
구조
1. Row 구조체 — FElementChargeRule
USTRUCT(BlueprintType)
struct RETRIEVE_API FElementChargeRule : public FTableRowBase
{
GENERATED_BODY()
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Gauge", meta=(Categories="GameplayEvent"))
FGameplayTag EventTag;
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Gauge", meta=(ClampMin="0"))
int32 ChargeAmount = 0;
};
2. 컴포넌트 멤버
UPROPERTY(EditDefaultsOnly, Category="Gauge|Charge")
TObjectPtr<UDataTable> ChargeRuleTable; // BP에서 DT 할당
TMap<FGameplayTag, int32> ChargeRuleCache; // 런타임 캐시
FGameplayTagContainer SubscribedFilter; // 구독 필터 (해제 시 동일 필터 필요)
FDelegateHandle GameplayEventHandle;
3. 구독 패턴 — AddGameplayEventTagContainerDelegate
GameplayEventHandle = ASC->AddGameplayEventTagContainerDelegate(
SubscribedFilter,
FGameplayEventTagMulticastDelegate::FDelegate::CreateUObject(
this, &UElementGaugeComponent::HandleGameplayEvent));
- 캐시 키들을 모두 컨테이너에 담아 단일 델리게이트로 구독 → 이벤트당 1회만 발동 (중복 방지)
- 해제: RemoveGameplayEventTagContainerDelegate(SubscribedFilter, GameplayEventHandle)
4. 핸들러 — 본인 발생 이벤트만 + 정확 매칭
void UElementGaugeComponent::HandleGameplayEvent(FGameplayTag EventTag, const FGameplayEventData* Payload)
{
if (!Payload || Payload->Instigator != GetOwner()) return; // 본인 발생만
if (const int32* Amount = ChargeRuleCache.Find(EventTag))
{
AddCharge(*Amount);
}
}
핵심 설계 결정
GAS 두 구독 API 비교
"정확 매칭" 정책
- 테이블에 Attack.HitSuccess.Light가 없고 Attack.HitSuccess만 있을 때, Light 이벤트가 들어오면 → 콜백은 발동(필터 부모 매칭)하지만 캐시 lookup에서 Light가 없어 skip
- 부모 fallback 안 함. Light/Heavy/WeakPoint 각각 다른 값으로 튜닝하려면 모두 행으로 등록
Instigator 가드
- 컨벤션: 이벤트 발사 측이 EventData.Instigator = GetOwner() 세팅 (EnemyCombatComponent, PatternCounterComponent 등 기존 코드 따름)
- 핸들러에서 Payload->Instigator != GetOwner() 면 스킵 → 피격, 보스 패턴 등이 잘못 게이지를 채우지 않음
- 주의: 신규 이벤트 발사 코드 작성 시 Instigator 반드시 세팅. 누락 시 게이지가 안 오름
바인딩 시점
- SovereignCharacter::InitializeAbilitySystem()에서 ASC 준비 완료 후 ElementGaugeComponent->BindToASC() 호출
- EndPlay에서 UnbindFromASC() 자동 해제 (Owner ASC 유효 시 RemoveDelegate)
데이터테이블 만들기
- 콘텐츠 브라우저 우클릭 → Miscellaneous → Data Table
- Row Structure: ElementChargeRule 선택
- 행 추가
- BP_SovereignCharacter → Components → ElementGaugeComponent → Details의 Gauge | Charge → Charge Rule Table에 DT 할당
트러블슈팅
- 게이지가 안 오름 → ① DT가 BP에 할당됐는지 ② 이벤트 발사 측에서 Instigator 세팅했는지 ③ BindToASC가 호출됐는지 (DT 미할당이면 no-op) ④ 캐시에 정확 일치 태그 행이 있는지
- 두 번 오름 → Instigator 가드 누락된 이벤트인지, 또는 같은 이벤트가 두 곳에서 발사되는지 확인