https://github.com/tranek/GASDocumentation#concepts-ga
GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer s
My understanding of Unreal Engine 5's GameplayAbilitySystem plugin with a simple multiplayer sample project. - GitHub - tranek/GASDocumentation: My understanding of Unreal Engine 5's Gamepl...
github.com
단순히 위의 문서를 보고 제멋대로 해석해서 적은 것입니다. 차라리 위의 문서를 봐주세요. 저 영어 못 합니다.
4.8 Gameplay Cues
4.8.1 Gameplay Cue Definition
GameplayCues는 게임플레이와는 관련없는 사운드 이펙트, 파티클 이펙트, 카메라 흔딜림 등과 같은 것을 수행한다. 게임플레이 큐는 일반적으로 replicate(unless explicitly Executed, Added, or Removed locally)되며 predicte된다.
게임플레이 태그의 필수적인 부모 이름과 맞는 게임플레이 태그와 이벤트 타입(Execute, Add or Remove)을 ASC를 통해 게임플레이 큐를 보내서 게임플레이 큐를 트리거한다. GameplayCueNotify 오브젝트와 IGameplayCueInterface를 구현하는 액터는 GameplayCue의 게임플레이 태그를 통해 구독할 수있다.
Note: 게임플레이 큐와 게임플레이 태그는 부모 게임플레이 큐의 게임플레이 태그가 필요하다. 예를 들어, 유효한 게임플레이 큐 게임플레이 태그는 GameplayCue.A.B.C이다.
GameplayCueNotifies에는 2가지 클래스가 있다. Static과 Actor. 그들은 서로 다른 이벤트와 다른 타입의 게임플레이 이펙트를 통해 트리거 된다. 로직에 따라 맞는 이벤트를 재정의하자.
GameplayCue Class | Event | GameplayEffect Type | Description |
GameplayCueNotifiy_Static | Execute | Instant or Periodic | Static GameplayCueNotifies는 ClassDefaultObject위에서 동작한다 (meaning no instances). 그리고 일회성 이펙트에 완벽하다. 히트 임팩트 |
GameplayCueNotifiy_Actor | Add or Remove | Duration or Infinite | GameplayCueNotifies 액터는 더해졌을 때, 새로운 인스턴스를 생성한다. 인스턴스화 되어있으므로 제거될 때까지 작업을 수행할 수 있다. 이런 방법은 반복되는 사운드나 파티클이펙트들을 Duration이나 infinite 게임플레이 이펙트가 제거될 때까지 혹은 수동적으로 제거될 때까지 계속 나타나는데 좋다. 또한 동시에 추가할 수 있는 개수를 관리하여 동일한 이펙트를 여러 사용처에서 사운드나 이펙트를 한번만 출력하게 하는 것도 가능하다. |
GameplayCueNotifies는 기술적으로 모든 이벤트에 응답할 수 있다.
Note: GameplayCueNotifiy_Actor를 사용할 때, Auto Destroy on Remove를 체크해야한다 .그렇지 않으면 뒤따른 게임플레이 큐 태그를 추가하는 요청이 작동하지 않는다.
When using an ASC Replication Mode other than Full, Add and Remove GC events will fire twice on Server players (listen server) - once for applying the GE and again from the "Minimal" NetMultiCast to the clients. However, WhileActive events will still only fire once. All events will only fire once on clients.
샘플 프로젝트에선 GameplayCueNotify_Actor는 스턴과 달리기 이펙트에 사용된다. 투사체의 임팩트에는 GameplayCueNotify_Static이 사용된다. 이러한 게임플레이 큐들은 GE를 통해 replicate하는 대신 로컬로 트리거하여 더욱 최적화 할 수 있다. 샘플 프로젝트에선 초보자적인 방법으로 하였다.
4.8.2 Triggering Gameplay Cues
게임플레이 이펙트가 tags나 immunity로 인해 막히지 않고 성공적으로 적용되었을 때, 게임플레이 이펙트 내부에 트리거할 게임플레이 큐의 게임플레이 태그를 채워넣는다.
UGameplayAbility는 블루프린트 노드를 통해 Gameplay Cue를 실행, 추가, 제거 할 수 있다.
C++에서는 ASC에서 직접적으로 호출할 수 있다. (or expose them to blueprint in your ASC subclass):
/** GameplayCues can also come on their own. These take an optional effect context to pass through hit result, etc */
void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());
void ExecuteGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
/** Add a persistent gameplay cue */
void AddGameplayCue(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());
void AddGameplayCue(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
/** Remove a persistent gameplay cue */
void RemoveGameplayCue(const FGameplayTag GameplayCueTag);
/** Removes any GameplayCue added on its own, i.e. not as part of a GameplayEffect. */
void RemoveAllGameplayCues();
4.8.3 Local Gameplay Cues
게임플레이 어빌리티와 ASC에서 게임플레이 큐를 실행하기 위해 노출된 함수는 기본적으로 replicate된다. 게임플레이 큐 이벤트는 멀티캐스트 RPC이다. 이것은 많은 양의 RPC를 발생시킨다. GAS는 또한 하나의 net 업데이트에서 최대 2개의 같은 게임플레이 큐를 적용한다. 우리는 우리가 가능한 곳에서는 local 게임플레이 큐의 사용을 피해야 한다. local 게임플레이 큐는 오직 개인의 클라이언트에서만 실행되고 추가되고 제거된다.
local 게임플레이 큐를 사용할 수 있는 시나리오:
- 투사체 충돌
- Melee 콜리전 충돌
- 애니메이션 몽타주를 실행하는 게임플레이 큐
ASC 하위클래스에 추가해야 하는 local 게임플레이 큐 함수:
UFUNCTION(BlueprintCallable, Category = "GameplayCue", Meta = (AutoCreateRefTerm = "GameplayCueParameters", GameplayTagFilter = "GameplayCue"))
void ExecuteGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
UFUNCTION(BlueprintCallable, Category = "GameplayCue", Meta = (AutoCreateRefTerm = "GameplayCueParameters", GameplayTagFilter = "GameplayCue"))
void AddGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
UFUNCTION(BlueprintCallable, Category = "GameplayCue", Meta = (AutoCreateRefTerm = "GameplayCueParameters", GameplayTagFilter = "GameplayCue"))
void RemoveGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters& GameplayCueParameters);
void UPAAbilitySystemComponent::ExecuteGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters & GameplayCueParameters)
{
UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::Executed, GameplayCueParameters);
}
void UPAAbilitySystemComponent::AddGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters & GameplayCueParameters)
{
UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::OnActive, GameplayCueParameters);
UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::WhileActive, GameplayCueParameters);
}
void UPAAbilitySystemComponent::RemoveGameplayCueLocal(const FGameplayTag GameplayCueTag, const FGameplayCueParameters & GameplayCueParameters)
{
UAbilitySystemGlobals::Get().GetGameplayCueManager()->HandleGameplayCue(GetOwner(), GameplayCueTag, EGameplayCueEvent::Type::Removed, GameplayCueParameters);
}
만약 게임플레이 큐가 locally하게 들어왔다면, locally하게 제거되어야 한다. 만약 replication을 통해 들어왔다면 replication을 통해 제거해야 한다.
4.8.4 Gameplay Cue Parameters
게임플레이 큐는 게임플레이큐의 추가적인 정보를 담고있는 FGameplayCueParameters 구조체를 파라미터로 받는다. 만약 수동적으로 어빌리티나 ASC의 함수에서 게임플레이 큐를 트리거했다면, 게임플레이 큐에 들어가는 GameplayCueParameters 구조체를 수동적으로 채워넣어야 한다. 만약 게임플레이 이펙트에 의해 큐가 트리거되었다면 GameplayCueParameters 구조체에 다음의 변수들이 자동적으로 채워진다.
- AggregatedSourceTags
- AggregatedTargetTags
- GameplayEffectLevel
- AbilityLevel
- EffectContext
- Magnitude(게임플레이 이펙트가 게임플레이 큐 태그 컨테이너 위의 드롭다운에서 선택한 크기에 대한 어티리뷰트와 해당 어트리뷰트에 영향을 미치는 Modifier를 가지고 있는 경우)
GameplayCueParameters 구조체안에 SourceObject 변수는 게임플레이 큐를 수동적으로 트리거하였을 때, 임의의 데이터를 게임플레이 큐로 집어넣는데 잠재적으로 좋은 위치이다.
Note: Instigator와 같은 파라미터 구조체의 몇몇의 변수는 EffectContext에 이미 존재하고 있을 수 있다. EffectContext는 월드에서 게임플레이 큐를 스폰시킬 위치를 위한 FHitResult를 가지고 있을 수 있다. EffectContext의 하위클래스를 만드는 것은 게임플레이 큐에 더 많은 데이터를 넣는데 잠재적으로 좋은 방법이다. 특히 게임플레이 이펙트에 의해 트리거되었을 경우.
자세한 내용은 GameplayCueParamters를 채우는 UAbilitySystemGlobals의 3가지 함수를 보자. 그들은 가상함수이다. 그러므로 더 많은 정보를 자동으로 넣으려면 이 것들을 재정의 해야 한다.
/** Initialize GameplayCue Parameters */
virtual void InitGameplayCueParameters(FGameplayCueParameters& CueParameters, const FGameplayEffectSpecForRPC &Spec);
virtual void InitGameplayCueParameters_GESpec(FGameplayCueParameters& CueParameters, const FGameplayEffectSpec &Spec);
virtual void InitGameplayCueParameters(FGameplayCueParameters& CueParameters, const FGameplayEffectContextHandle& EffectContext);
4.8.5 Gameplay Cue Manager
기본적으로 GameplayCueManager는 GameplayCueNotifies를 전체 게임 디렉토리에서 스캔한다. 그리고 플레이 시 메모리에 로드한다. DefaultGame.ini에서 GameplayCueManage가 스캔할 경로를 바꿀 수 있다.
[/Script/GameplayAbilities.AbilitySystemGlobals] GameplayCueNotifyPaths="/Game/GASDocumentation/Characters"
GameplayCueManager가 모든 GameplayCueNotifies를 찾고 스캔하길 원한다. 하지만 플레이 시 각각이 비동기식으로 로드되는 것을 원하지는 않는다. 그 레벨에서 사용하든 안 하든, 모든 GameplayCueNotifiy와 거기서 참조되는 사운드와 파티클들은 메모리에 실려질 것이다. 파라곤처럼 대규모의 게임에서는 몇 백 메가바이트의 필요없는 에셋이 메모리에 로드될 것이고 게임 시작 시 프리즈와 hitching이 발생한다.
플레이 시 모든 게임플레이 큐를 비동기적으로 로드하는 방법은 인게임에서 트리거되었을 때, 게임플레이 큐를 비동기적으로 로드하는 것이다. This mitigates the unnecessary memory usage and potential game hard freezes while async loading every GameplayCue in exchange for potentially delayed effects for the first time that a specific GameplayCue is triggered during play. 이런 잠재적인 딜레이는 SSD에는 존재하지 않는다. HDD에서는 테스트한 적이 없다. 만약 UE 에디터에서 이 옵션을 사용한다면, 에디터가 파티클 시스템을 컴파일해야 하는 경우 게임플레이 큐의 첫 번째 로드 동안 약간의 걸림이나 프리즈 현상이 발생할 수 있다. 파티클 시스템이 이미 컴파일된 빌드에서는 이슈가 아니다.
먼저 우리는 UGameplayCueManager의 하위 클래스를 만들어서 DefaultGame.ini에서 우리의 UGameplayCueManager를 사용한다고 AbilitySystemGlobals클래스에 말해야 한다.
[/Script/GameplayAbilities.AbilitySystemGlobals] GlobalGameplayCueManagerClass="/Script/ParagonAssets.PBGameplayCueManager"
우리의 UGameplayCueManager 하위 클래스에서, ShouldAsyncLoadRuntimeObjectLibraries()함수를 재정의 한다.
virtual bool ShouldAsyncLoadRuntimeObjectLibraries() const override
{
return false;
}
4.8.6 Prevent Gameplay Cues from Firing
종종 게임플레이 큐의 실행을 막고싶을 수 있다. 예를 들어, 공격을 막았을 때, 데미지 게임플레이 이펙트에 있는 충돌 임팩트를 플레이하지 않길 원할 수도 있고, 커스텀된 임팩트를 출력하길 원할 수 있다. 우리는 OutExecutionOutput.MarkGameplayCuesHandleManually()를 호출하여 GameplayEffectExecutionCalculations안에서 이걸 할 수 있다. 그리고 수동적으로 우리의 게임플레이 큐 이벤트를 타겟이나 소스의 ASC에 보낸다.
만약 특정 ASC에 어떤 게임플레이 큐도 실행하고 싶지 않다면 AbilitySystemComponent->bSuppressGameplayCues = trues;로 설정하면 된다.
4.8.7 Gameplay Cue Batching
각각의 트리거된 게임플레이 큐는 믿을 수 없는 NetMulticast RPC이다. 같은 시간에 여러개의 게임플레이 큐를 실행한 상황에서, 하나의 RPC로 줄이거나 데이터 전송을 줄여서 대역폭을 아끼는 최적화 방법이 있다.
4.8.7.1 Manual RPC
8개의 총알을 날리는 샷건이 있다면 8개의 라인트레이스와 8개의 임팩트 게임플레이 큐가 발생할 것이다. GASShooter에서는 모든 트레이스 정보를 TargetData로서 EffectContext로 스태이싱하여 하나의 RPC로 결합하는 게으른 방식을 취하고 있다. 이 방법은 8개를 1개로 줄이지만, 하나의 RPC(~500바이트)를 통해 너무 많은 데이터를 보내고 있다. 더 최적화한 접근 방식은 맞은 위치를 효율적으로 인코드한 커스텀 구조체를 RPC에 보내거나 받는 측에서 어림짐작하여 맞은 위치를 다시 만들 수 있도록 랜덤 시드 넘버를 보내는 방법이다. 클라이언트는 이 커스텀 구조체를 언팩하고 locally 실행된 게임플레이 큐로 변환한다.
어떻게 돌아가는가:
- FScopedGameplayCueSendContext를 정의한다. 이것은 UGameplayCueManager::FlushPendingCues()를 억압하고 FScopedGameplayCueSendContext가 범위에서 벗어날 때까지, 모든 게임플레이 큐가 대기열에 들어간다는 것을 의미한다.
- UGameplayCueManger::FlushPendingCues()를 재정의한다. 여기서 몇몇의 커스텀 게임플레이 태그를 기반으로 함께 batch될 수 있는 게임플레이 큐를 커스텀 구조체로 모으고 클라이언트에 RPC한다.
- 클라이언트는 커스텀 구조체를 받고 locally 실행된 게임플레이 큐로 언팩한다.
이러한 방법은 우리의 게임플레이 큐에 특정한 파라미터가 필요할 경우에도 쓰일 수 있다. 게임플레이 큐 파라미터 제공이 우리의 게임플리이 큐에 맞지않는 경우. 데미지 넘버나, 크리티컬 히트 indicator나 방패 부서짐 indicator과 같은 EffectContext에 추가하지 않고 싶을 때.
4.8.7.2 Multiple GCs on one GE
모든 게임플레이 이펙트의 게임플레이 큐는 이미 하나의 RPC를 보낸다. 기본적으로 UGameplayCueManager::InvokeGameplayCueAddedAndWhileActive_FromSpec()은 전체 GameplayEffectSpec(하지만 FGameplayEffectSpecForRPC로 변경된)를 ASC의 Replication Mode에 상관없이 신용할 수 없는 NetMulticast로 보낸다. 이것은 게임플레이 이펙트 스펙에 무엇이 있느냐에따라 잠재적으로 아주 많은 대역폭을 소모한다. 우리는 AbilitySystem.AlwaysConvertGESpecToGCParams 1을 세팅하여 잠재적으로 이것을 최적화할 수 있다. 이것은 GameplayEffectSpecs를 FGameplayCueParameter 구조체로 변형할 것이다. 그리고 전체 FGameplayEffectSpecForRPC가 RPC로 변형된다. 이것은 잠재적으로 대역폭을 아낄 수 있지만 어떻게 GESpec이 GameplayCueParameters로 변형되었는지, GCs가 필요한 것에 따라 더 적은 정보를 가진다.
4.8.8 Gameplay Cue Events
게임플레이 큐는 특정 EGameplayCueEvents에 반응한다:
EGameplayCueEvent | Description |
OnActive | 게임플레이 큐가 활성화(더하져)되었을 때 호출된다. |
WhileActive | 게임플레이 큐가 실제로 적용되지 않아도 (join inprogress, etc)활성화 되었을 때 호출된다. This is not Tick! GameplayCueNotify_Actor가 더해졌거나 관련있을 때, OnActive처럼 한번만 호출된다. Tick()을 사용하고 싶다면 GameplayCueNotify_Actor의 Tick()을 사용해라. GameplayCueNotify_Actor는 액터다. |
Removed | 게임플레이 큐가 제거되었을 때 호출된다. 블루프린트에서 해당하는 것은 OnRemove이다. |
Executed | 게임플레이 큐가 실행되었을 때 호출한다: instant 이펙트나 periodic Tick(). 블루프린트에서 해당하는 것은 OnExecute이다. |
Use OnActive for anything in your GameplayCue that happen at the start of the GameplayCue but is okay if late joiners miss. Use WhileActive for ongoing effects in the GameplayCue that you would want late joiners to see. 예를 들어, MOBA에서 타워가 폭발하는 게임플레이 큐가 있다면 첫 폭발 파티클 시스템과 폭발 사운드를 OnActive에 둘 수 있다. 그리고 잔불 파티클과 사운드는 WhileActive에 둔다. 이 시나리오에서 late joiners가 OnActive에서 첫 폭발을 재생하는 것은 말이 안되지만, 폭발이 일어난 후 WhileActive에서 지속되는 불 이펙트를 보고싶을 것이다. OnRemove는 OnActive와 WhileActive에 추가된 모든 것들을 정리해야 한다. WhileActive는 GameplayCueNotify_Actor의 relevancy 범위안에 들어올 때마다 호출될 것이다. OnRemove는 GameplayCueNotify_Actor의 relevancy 범위안에서 나갈 때마다 호출될 것이다.
4.8.9 Gameplay Cue Reliablity
게임플레이 큐는 일반적으로 unreliable로 여겨진다. 그러므로 게임플레이에 직접적으로 영향을 미치는 것에는 맞지 않다.
Executed GameplayCues: 이 게임플레이 큐는 unreliable 멀티캐스트를 통해 적용되며 항상 unreliable이다.
게임플레이 이펙트에서부터 적용된 게임플레이 큐:
- Autonomous Proxy는 OnActive, WhileActive 및 OnRemove FActiveGameplayEffectsContainer::NetDeltaSerialize()를 안정적으로 받는다. FActiveGameplayEffectsContainer::NetDeltaSerialize()는 OnActive와 WhileActive를 호출하기위해 호출한다.
- Simulated Proxies는 WhileActive와 OnRemove UAbilitySystemComponent::MinimalReplicationGameplayCue의 replication을 안정적으로 받는다. UAbilitySystemComponent::MinimalReplicationGameplayCue의 replication은 WhileActive과 OnRemove를 호출한다. OnActive이벤트는 unreliable 멀티캐스트에 의해 호출된다.
게임플레이 이펙트 없이 적용된 게임플레이 큐
- Autonomous Proxy는 unreliable 멀티캐스트에 의해 호출된 OnActive와 WhileActive인 OnRemove 이벤트를 reiably 받는다.
- Simulated proxies는 WhileActive와 OnRemove를 호출하는 UAbilitySystemComponent::MinimalReplicationGameplayCues의 WhileActive와 OnRemove 이벤트를 reliably 받는다.
게임플레이 큐 안에 'reliable' 뭔가가 필요하다면, 게임플레이 이펙트로부터 적용하고, FX를 추가하기 위해 WhileActive를 사용하고 FX를 제거하기위해 OnRemove를 사용하라.
'Unreal Engine > Gameplay Ability System' 카테고리의 다른 글
[UE] Gameplay Ability System -9- Prediction (0) | 2023.06.03 |
---|---|
[UE] Gameplay Ability System -8- Abilty System Globals (0) | 2023.06.02 |
[UE] Gameplay Ability System -6- Ability Task (0) | 2023.05.30 |
[UE] Gameplay Ability System -5- Gameplay Ability (0) | 2023.05.30 |
[UE] Gameplay Ability System -4- Gameplay Effects (0) | 2023.05.28 |