콘텐츠로 이동

GAS Gameplay Cue 시스템

GameplayCue는 GAS(Gameplay Ability System)에서 시각·청각 피드백(이펙트, 사운드, 카메라 흔들림 등)을 게임플레이 로직과 분리하는 시스템입니다. GameplayEffect나 GameplayAbility가 Cue를 트리거하면, Cue 핸들러가 클라이언트에서만 실행되어 서버 부하를 줄입니다.


GameplayCue 태그는 반드시 GameplayCue. 접두사로 시작해야 합니다.

GameplayCue.Character.Hit
GameplayCue.Character.Death
GameplayCue.Weapon.FireBullet
GameplayCue.Environment.Explosion.Small
GameplayCue.Environment.Explosion.Large
GameplayCue.Buff.Speed.Active

태그는 DefaultGameplayTags.ini 또는 Project Settings > GameplayTags에 등록합니다.


종류트리거 시점사용 예
OnActiveGameplayEffect 적용 시작버프 시작 파티클 재생
WhileActive적용 중 (틱 가능)지속 버프 이펙트 유지
RemovedGameplayEffect 제거버프 종료 이펙트
Executed즉발 (Instant GE 또는 직접 실행)히트 임팩트, 폭발

GameplayCue_HitImpact.h
#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;
};
GameplayCue_HitImpact.cpp
#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: 기본 동작 처리 완료
}

GameplayCue_SpeedBuff.h
#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;
}

// GameplayEffect 에셋의 "Gameplay Cues" 섹션에 태그 추가
// GameplayCue.Character.SpeedBuff → 태그 등록 후 GE 적용 시 자동 실행
// 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 에셋은 비동기 로드로 스터터링을 방지합니다.

[/Script/GameplayAbilities.AbilitySystemGlobals]
// 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에서 사전 비동기 로드