GAS Gameplay Cue 시스템
GameplayCue는 GAS(Gameplay Ability System)에서 시각·청각 피드백(이펙트, 사운드, 카메라 흔들림 등)을 게임플레이 로직과 분리하는 시스템입니다. GameplayEffect나 GameplayAbility가 Cue를 트리거하면, Cue 핸들러가 클라이언트에서만 실행되어 서버 부하를 줄입니다.
Cue 태그 네이밍 규칙
섹션 제목: “Cue 태그 네이밍 규칙”GameplayCue 태그는 반드시 GameplayCue. 접두사로 시작해야 합니다.
GameplayCue.Character.HitGameplayCue.Character.DeathGameplayCue.Weapon.FireBulletGameplayCue.Environment.Explosion.SmallGameplayCue.Environment.Explosion.LargeGameplayCue.Buff.Speed.Active태그는 DefaultGameplayTags.ini 또는 Project Settings > GameplayTags에 등록합니다.
Cue 종류
섹션 제목: “Cue 종류”| 종류 | 트리거 시점 | 사용 예 |
|---|---|---|
OnActive | GameplayEffect 적용 시작 | 버프 시작 파티클 재생 |
WhileActive | 적용 중 (틱 가능) | 지속 버프 이펙트 유지 |
Removed | GameplayEffect 제거 | 버프 종료 이펙트 |
Executed | 즉발 (Instant GE 또는 직접 실행) | 히트 임팩트, 폭발 |
C++ Cue 핸들러 구현
섹션 제목: “C++ Cue 핸들러 구현”#pragma once#include "GameplayCueNotify_Static.h"#include "GameplayCue_HitImpact.generated.h"
// Static Cue — 상태 없는 즉발 이펙트 (Executed용)UCLASS()class MYGAME_API UGameplayCue_HitImpact : public UGameplayCueNotify_Static{ GENERATED_BODY()
public: virtual bool OnExecute_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters) const override;
protected: UPROPERTY(EditDefaultsOnly, Category = "Effects") UParticleSystem* ImpactParticle;
UPROPERTY(EditDefaultsOnly, Category = "Effects") USoundBase* ImpactSound;
UPROPERTY(EditDefaultsOnly, Category = "Effects") TSubclassOf<UCameraShakeBase> CameraShake;};#include "GameplayCue_HitImpact.h"#include "Kismet/GameplayStatics.h"#include "GameFramework/PlayerController.h"
bool UGameplayCue_HitImpact::OnExecute_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters) const{ if (!MyTarget) return false;
FVector Location = Parameters.Location; FRotator Rotation = Parameters.Normal.Rotation();
// 파티클 스폰 if (ImpactParticle) { UGameplayStatics::SpawnEmitterAtLocation( MyTarget->GetWorld(), ImpactParticle, Location, Rotation); }
// 사운드 재생 if (ImpactSound) { UGameplayStatics::PlaySoundAtLocation( MyTarget->GetWorld(), ImpactSound, Location); }
// 카메라 흔들림 (로컬 플레이어에게만) if (CameraShake) { APlayerController* PC = MyTarget->GetWorld()->GetFirstPlayerController(); if (PC && PC->IsLocalController()) { PC->ClientStartCameraShake(CameraShake); } }
return true; // true: 기본 동작 처리 완료}Actor Cue — 상태 유지 이펙트
섹션 제목: “Actor Cue — 상태 유지 이펙트”#pragma once#include "GameplayCueNotify_Actor.h"#include "GameplayCue_SpeedBuff.generated.h"
// Actor Cue — WhileActive/OnActive/Removed 이벤트 지원UCLASS()class MYGAME_API AGameplayCue_SpeedBuff : public AGameplayCueNotify_Actor{ GENERATED_BODY()
public: // GameplayEffect 적용 시작 virtual bool OnActive_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters) override;
// GameplayEffect 유지 중 (틱마다) virtual bool WhileActive_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters) override;
// GameplayEffect 제거 virtual bool OnRemove_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters) override;
protected: UPROPERTY(EditDefaultsOnly) UNiagaraSystem* SpeedTrailEffect;
private: UPROPERTY() UNiagaraComponent* ActiveEffect;};#include "GameplayCue_SpeedBuff.h"#include "NiagaraFunctionLibrary.h"#include "NiagaraComponent.h"
bool AGameplayCue_SpeedBuff::OnActive_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters){ if (SpeedTrailEffect && MyTarget) { ActiveEffect = UNiagaraFunctionLibrary::SpawnSystemAttached( SpeedTrailEffect, MyTarget->GetRootComponent(), NAME_None, FVector::ZeroVector, FRotator::ZeroRotator, EAttachLocation::SnapToTarget, true); } return true;}
bool AGameplayCue_SpeedBuff::OnRemove_Implementation( AActor* MyTarget, const FGameplayCueParameters& Parameters){ if (ActiveEffect) { ActiveEffect->Deactivate(); ActiveEffect = nullptr; } return true;}GameplayCue 트리거 방법
섹션 제목: “GameplayCue 트리거 방법”GameplayEffect를 통한 자동 트리거
섹션 제목: “GameplayEffect를 통한 자동 트리거”// GameplayEffect 에셋의 "Gameplay Cues" 섹션에 태그 추가// GameplayCue.Character.SpeedBuff → 태그 등록 후 GE 적용 시 자동 실행C++ 코드에서 직접 트리거
섹션 제목: “C++ 코드에서 직접 트리거”// Ability 내부에서 직접 실행void UMyGameplayAbility::ActivateAbility( const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData){ // Cue 파라미터 구성 FGameplayCueParameters CueParams; CueParams.Location = GetAvatarActorFromActorInfo()->GetActorLocation(); CueParams.Normal = FVector::UpVector; CueParams.SourceObject = this; CueParams.EffectCauser = GetAvatarActorFromActorInfo();
// 즉발 Cue 실행 FGameplayTag CueTag = FGameplayTag::RequestGameplayTag("GameplayCue.Weapon.FireBullet"); ActorInfo->AbilitySystemComponent->ExecuteGameplayCue(CueTag, CueParams);}네트워크 동작
섹션 제목: “네트워크 동작”서버 클라이언트 | | | GameplayEffect 적용 | |──────────────────────────────▶| | Cue 실행 (로컬) | | | (서버는 Cue 로직 실행 안 함) |- GameplayCue는 클라이언트에서만 실행됩니다.
bAutoAttachToOwner가 true인 Actor Cue는 소유자 액터에 자동 부착됩니다.- 전용 서버(dedicated server)에서는
UGameplayCueManager::ShouldSuppressCues()가 true를 반환해 Cue가 실행되지 않습니다.
Cue Manager & 사전 로딩
섹션 제목: “Cue Manager & 사전 로딩”대량의 Cue 에셋은 비동기 로드로 스터터링을 방지합니다.
// GameplayCueManager를 서브클래스로 교체 (DefaultGame.ini)// GameplayCueManagerClassName=/Script/MyGame.MyGameplayCueManager
UCLASS()class UMyGameplayCueManager : public UGameplayCueManager{ GENERATED_BODY()
public: // 게임 시작 시 자주 쓰는 Cue 사전 로드 virtual void OnEngineInitComplete() override { Super::OnEngineInitComplete(); LoadNotifyForGameplayCueTag( FGameplayTag::RequestGameplayTag("GameplayCue.Character.Hit"), EGameplayCueEvent::Executed, FStreamableDelegate()); }};핵심 요약
섹션 제목: “핵심 요약”- 태그는
GameplayCue.접두사 필수 - 즉발 이펙트 →
UGameplayCueNotify_Static(OnExecute) - 지속 이펙트 →
AGameplayCueNotify_Actor(OnActive/WhileActive/OnRemove) - Cue는 클라이언트 전용 — 서버 부하 없음
- 직접 트리거 →
ExecuteGameplayCue/AddGameplayCue - 대량 에셋은 CueManager에서 사전 비동기 로드