https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-p

 

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. - tranek/GASDocumentation

github.com

Definition

  • GAS는 Client 쪽에 Prediction을 기본적으로 제공한다.
    • 하지만 모두 다 Predict 되는 것은 아니다.
  • Client에서 Predict 된다는 의미는 Server에서 Gameplay Ability 활성화나 Gameplay Effect 등록에 대한
    허가를 기다릴 필요가 없다는 것을 의미한다.
  • 이는 작업을 수행할 권한을 부여해준 Server와 Gameplay Effect가 적용 될 Target을 Predict 하는 것이다.
  • Server는 Client가 활동하고 나서 네트워크 지연 시간이 지난 후 GameplayAbility를 실행한 후,
    Client에 Predict로 시행한 동작이 맞는지 틀린지 답해준다.
    • 만약 Client가 틀렸다면, Client에서 잘못 Predict 한 것을 Server의 결과로 복구한다.
  • GAS에서의 Predict 관련된 코드들은 GameplayPrediction.h 파일에 있다.
  • Epic의 Predict에 대한 철학은 "해도 될 것 같은 것"에 사용하는 것이다.
    • 예를 들어 Paragon이나 Fortnite는 Damage를 Predict 하지 않는다.
    • 대부분 ExecutionCalculations를 통해 Predict 할 수 없는 Damage를 처리한다.
... we are also not all in on a "predict everything: seamlessly and automatically" solution. We still feel player prediction is best kept to a minimum (meaning: predict the minimum amount of stuff you can get away with).
  • Dave Ratti from Epic's comment from the new Network Prediction Plugin

Predict 가능한 것

  • Ability Activation
  • Triggered Events
  • GameplayEffect Application
    • Attribute Modification(Not Executions, only Attribute Modification)
    • GameplayTag Modification
  • Gameplay Cue Events
  • Montages
  • Movement(in UCharacterMovement)

Predict 불가능한 것

  • GameplayEffect Removal
  • GameplayEffect periodic Effect

추가 설명

  • GameplayEffect 등록은 Predict 할 수 있지만, 제거는 Predict 할 수 없다.
    • 이 제약을 피하는 한가지 방법은 제거 대신 Inverse Effect를 Predict하는 것이다.
  • GameplayEffect 제거를 Predict 할 수 없기 때문에, 우리는 GameplayEffect Cooldown을 Predict 할 수 없다.
    • 심지어 이건 Inverse도 없다.
    • Server에서 Replicate 된 Cooldown GE는 Client에 존재 할 것이고
      이를 제거하려는 모든 시도는 Server에 의해 거부될 것이다.
    • 이는 지연시간이 긴 유저가 서버로부터 Cooldown을 받는데 걸리는 시간이 길어 더 불리하다는 얘기이다.
      • Fortnite는 이를 피하기 위해 Cooldown GE 대신 Custom Bookkeeping을 사용하고 있다.
  • Instant GE를 자신에게 사용할 때에는 문제 없지만, 타인에게 사용할 때에는 값이 잠깐 튈 수 있다.
    • Instant GE를 Predict 하는 것은 Infinite GE처럼 취급되어 복구가 될 수 있다.
    • 만약 Server의 GE가 적용되면, 아주 잠깐동안 동시에 2개의 GE가 존재하여 Modifier가 2번 적용될 수 있다.
    • 이는 곧 정상화가 되겠지만, 일부 유저들은 이상현상을 감지할 수 있다.

PredictionKey

  • GA의 Prediction은 Client가 Gameplay Ability를 활성화 할 때 생성되는 Integer Identifier를 기반으로 동작한다.
    • Gameplay Ability가 활성화 될 때 Prediction Key가 생성된다.
      • 이를 Activation Prediction Key라 지칭한다.
    • Client는 이 Key를 CallServerTryActivateAbility() 함수를 통해 Server에 전달한다.
    • Client는 이 Key가 Valid한 동안 등록된 모든 Gameplay Effect에 추가한다.
    • Client의 Prediction Key가 Scope를 벗어난다.
      • 같은 Gameplay Ability에 Predict 한 Effect는 새 Scoped Prediction Window가 필요하다.
    • Serverrk Prediction Key를 Client로부터 전달 받는다.
    • Server가 Prediction Key를 등록된 모든 Gameplay Effect에 전달한다.
    • Server가 Prediction Key를 Client로 다시 Replicate 한다.
    • Client는 GE를 등록하는데 사용한 Prediction Key와 함께 GE를 Replicate 받는다.
      • 만약 Client에서 등록한 GE 중 Replicate 받은 GE와 동일한 것이 있다면, Predict가 성공한 것이다.
      • Client가 Predict한 GE를 제거하기 전까지 한 Target에 대해 일시적으로 2개의 GE가 존재하게 된다.
    • Client가 Server로부터 Prediction Key를 받는다.
      • 이것은 Replicated Prediction Key라 부른다.
      • 이 Prediction Key는 이제 Stale 상태로 둔다.
    • Client는 Stale 상태의 Replicated Prediction Key을 이용해 모든 GE를 지운다.
      • Server로부터 Replicate 받은 GE는 유지가 될 것이다.
      • Client가 생성했지만 Server로부터 Replicated Prediction Key를 받지 못한 GE는 모두 Predict 실패 처리된다.
  • Prediction Key는 GameAbility가 Activation Prediction Key로 인해 활성화 되고 나서
    Window라는 Atomic Group 명령이 수행되는 동안 Valid를 보장 받는다.
    • 단순하게 한 Frame동안 Valid를 보장 받는다 생각하면 된다.
    • 새로운 Scoped Prediction Window를 생성하는 Synch Point를 가지고 있지 않다면
      예정되어 있는 Ability Task의 모든 Callback은 Valid한 Prediction Key를 가질 수 없다.

Creating New Prediction Windows in Ability

  • Ability Task에서 발생한 Callback에 대해 더 많은 Predict를 하기 위해, 
    새 Scoped Prediction Key를 이용해 새 Scoped Prediction Window를 만들 필요가 있다.
    • 이는 Client와 Server 사이의 Synch Point로 여겨지기도 한다.
  • Input과 같은 일부 Ability Task에는 새 Scoped Prediction Window를 생성하는 기능이 내장되어 있다.
    • 이는 Ability Task의 Callback의 단위 코드는 유효한 Scoped Prediction Key를 가지고 있다는 뜻이다.
  • 만약 Ability Task 이후의 행동을 Predict 한다면,
    WaitDelay와 같이 Scoped Prediction Window를 만드는 코드가 없을 것이다.
    • 때문에 OnlyServerWait 옵션과 함께 AbilityTask관련하여 WaitNetSync을 사용해야만 한다.
  • Client가 OnlyServerWait 옵션인 채로 WaitNetSync를 호출하면 다음 동작을 시행한다.
    • Gameplay Ability의 Activation Prediction Key를 기반으로 한 새 Scoped Prediction Key를 생성
    • 새 Scoped Prediction Key를 Server에 RPC로 전달하고
    • 등록된 GE에 새 Scoped Prediction Key 추가
  • Server가 OnlyServerWait 옵션인채로 WaitNetSync를 호출하면 다음 동작을 시행한다.
    • 작업하기 전 Client로부터 새 Scoped Prediction Key를 받을 때까지 대기
  • 이 Scoped Prediction Key는 GE에 등록된 Activation Prediction Keys와 일치하고,
    Client에 Stale 한 채로 Replicate 된다.
  • Scoped Prediction Key는 Scope를 벗어나기 전까지 유효하다.
    • 이는 Scoped Prediction Window는 닫혀 있다는 것이다.
    • 그렇기에 미루어지지 않는 Atomic 연산만이 새 Scoped Prediction Key를 사용할 수 있다.
  • WaitNetSync를 사용하면, Server의 Gameplay Ability가 Client로부터 패킷을 받을 때까지 대기상태에 빠질 수 있다.
    • 이는 해커에게 취약점을 제공할 수 있다.

Predictively Spawning Actor

  • Client에서 Actor Spawn을 Predict 하는 것은 좀 더 복잡한 이슈이다.
    • 적어도 GAS는 이러한 기능을 제공하지는 않는다.
    • 중요한건 Replicate 되는 Actor을 Client와 Server 양쪽에 모드 Spawn 해야 한다는 것이다.
  • 만약 Actor가 장식이거나 Gameplay 목적이 전혀 없다면, IsNetRelavantFor() 함수를 Override 하여
    Server가 소유한 Client에 Replicate하지 않도록 하는 것도 방법이다.
    • Owner Client는 Local 상으로 Spawn을 하고, Server나 다른 Client는 Spawn을 하지 않는다.
더보기
bool APAReplicatedActorExceptOwner::IsNetRelevantFor(const AActor * RealViewer, const AActor * ViewTarget, const FVector & SrcLocation) const
{
	return !IsOwnedBy(ViewTarget);
}
  • 만약 Projectile처럼 Gameplay에 영향을 주는 Actor인 경우, 좀 더 복잡한 추가 작업이 필요하다.
    • Unreal Tournament에서는 Client에 Dummy Projectile을 Spawn하고
      Server의 Replicated Projecile와 Synch up을 하여 Actor Spawn Predict를 수행한다.

Future of Prediction in GAS

  • GameplayPrediction.h는 차후에 다음 항목에 대한 기능을 제공할 예정이라 밝혔다.
    • GameplayEffect Removal
    • Periodic Gameplay Effect
  • 또한 Dave Ratti는 Cooldown Prediction에서의 Latency로 인한 불균형 문제 수정에 관심을 보이기도 했다.
  • Epic의 새 Network Prediction Plugin은 예전 CharacterMovementComponent와 같이
    GAS와 상호운영이 될 것으로 예상된다.

Network Prediction Plugin

https://dev.epicgames.com/documentation/en-us/unreal-engine/API/Plugins/NetworkPrediction?application_version=5.3

 

Network Prediction | Unreal Engine 5.4 Documentation | Epic Developer Community

 

dev.epicgames.com

 

  • Epic은 CharacterMovementComponent를 새로운 Network Prediction Plugin으로 대체하는 작업을 시작했다.
  • 이 Plugin은 아직 매우 초창기이지만 Unreal Engine Github에서 쉽게 접근할 수 있다.
  • 그럼에도 도입 시기를 논하기에는 매우 이른 감이 없지않아 있다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Gameplay Cue  (0) 2024.05.21
[GAS] Gameplay Ability  (0) 2024.05.17
[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-gc

 

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. - tranek/GASDocumentation

github.com

Definition

  • Gameplay와 직접적인 관계가 없는 것들을 실행
    • Sound, Particle, Camer Shake 등
  • 보통은 Replicate되고, predict 가능하다.
  • Gameplay Cue의 필수 상위 이름이 포함된 Gameplay Tag와 EventType을
    ASC를 통해 GameplayCueManager에 전송해 실행한다.
  • 다음 조건을 만족하는 것들은 GameplayCue의 GameplayTag를 기반으로 한 이벤트를 구독할 수 있다.
    • GameplayCueNotify 객체
    • IGameplayCueInterface를 Implement 한 Actor

GameplayCueNotify Class

  • GameplayCueNotify는 어느 것이든 어떤 Event에도 반응하도록 작업이 되어 있다.
    • 다만 특정 Event에 더 적합한 GameplayCueNotify를 제공하고 있다.

GameplayCueNotify_Static

  • GameplayCue와 관련된 정보를 저장하지 않고 즉시 실행 하는 경량 Notify
    • ClassDefaultObject에서 작업이 이루어짐
  • Instant나 Periodic GE와 같이 한번만 즉발로 효과가 나타나야 하는 케이스에 적합함

GameplayCueNotify_Actor

  • GameplayCue와 관련된 정보를 저장할 수 있음
    • Spawn 될 때 새 Instance를 생성
    • 생성된 Instance를 통해 Remove 시점에 필요한 정보를 확인
  • 또한 동시에 얼마나 많은 GameplayCue가 존재할 수 있는지에 대한 정보를 지정할 수도 있다.
  • Duration이나 Infinite와 같이 종료 시점이 정해져 있거나, 그 시점에 어떤 행동을 요구할 때 적합함.
  • AutoDestoryOnRemove 옵션 값을 유의해서 사용해야 한다.
    • 활성화 되어 있을 시 Add에서 발생하는 후속 작업이 호출되지 않을 수 있다.
  • ASC의 Replication Mode가 Full이 아닌 경우,
    Listen Server 환경에서 Add와 Remove 이벤트가 Server에 2번 발생한다.
    • 한 번은 Gameplay Event가 등록될 때
    • 다른 한번은 Client에 Minimal NetMultiCast가 발동할 때.
  • 하지만 WhileActive 이벤트는 여전히 한번만 발생한다.
  • Client에서는 모든 Event가 1번씩만 발생한다.

Trigger Gameplay Cue

  • 내부에서 GE가 성공적으로 등록되면, 실행되어야만 하는 GameplayCue의 모든 GmeplayTag가 채워진다.

  • UGameplayAbility에서는 GameplayCue에 대한 Execute/Add/Remove BP Node를 제공한다.
  • C++에서는 아래 함수들을 통해 사용할 수 있다.
더보기
/** 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);

/** Add gameplaycue for minimal replication mode. Should only be called in paths that would replicate gameplaycues in other ways (through GE for example) if not in minimal replication mode */
void AddGameplayCue_MinimalReplication(const FGameplayTag GameplayCueTag, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());

/** Remove a persistent gameplay cue */
void RemoveGameplayCue(const FGameplayTag GameplayCueTag);

/** Remove gameplaycue for minimal replication mode. Should only be called in paths that would replicate gameplaycues in other ways (through GE for example) if not in minimal replication mode */
void RemoveGameplayCue_MinimalReplication(const FGameplayTag GameplayCueTag);

/** Removes any GameplayCue added on its own, i.e. not as part of a GameplayEffect. */
void RemoveAllGameplayCues();

/** Handles gameplay cue events from external sources */
void InvokeGameplayCueEvent(const FGameplayEffectSpecForRPC& Spec, EGameplayCueEvent::Type EventType);
void InvokeGameplayCueEvent(const FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, FGameplayEffectContextHandle EffectContext = FGameplayEffectContextHandle());
void InvokeGameplayCueEvent(const FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& GameplayCueParameters);

Local Gameplay Cues

  • ASC와 Gameplay Ability에서 제공하는 GameplayCue 실행 함수는 기본적으로 replicate 된다.
    • 각 GameplayCue Event는 multicast RPC이다.
더보기
// Do not call these functions directly, call the wrappers on GameplayCueManager instead
UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueExecuted_FromSpec(const FGameplayEffectSpecForRPC Spec, FPredictionKey PredictionKey) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueExecuted(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCuesExecuted(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueExecuted_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCuesExecuted_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueAdded(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayEffectContextHandle EffectContext) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueAdded_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters Parameters) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueAddedAndWhileActive_FromSpec(const FGameplayEffectSpecForRPC& Spec, FPredictionKey PredictionKey) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCueAddedAndWhileActive_WithParams(const FGameplayTag GameplayCueTag, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;

UFUNCTION(NetMulticast, unreliable)
void NetMulticast_InvokeGameplayCuesAddedAndWhileActive_WithParams(const FGameplayTagContainer GameplayCueTags, FPredictionKey PredictionKey, FGameplayCueParameters GameplayCueParameters) override;
  • 이는 많은 RPC를 야기하는데,
    GAS는 동일한 Gameplay Cue RPC가 net update 당 최대 2개만 발생하도록 제한하고 있다.
    • 우리는 이 문제를 Local Gameplay Cue를 사용함으로 해결할 수 있다.
      • Local Gameplay Cue는 GAS에서 제공하는 기능이 아니라 Gameplay Cue 사용 권장 사항이다.
    • Local Gameplay Cue는 각 Client에서 오직 Execute, Add, Remove를 시행한다.
  • Local GameplayCue를 호출하기 적절한 케이스는 다음과 같다.
    • 투사체 
    • 물리 공격 
    • Animation Montage에서 발생하는 GameplayCue
  • 만약 GameplayCue가 Local에서 Add 되었다면, Remove 역시 Local에서 수행 되어야만 한다.

Gameplay Cue Parameter

  • GameplayCue는 외부 정보를 FGameplayCueParameters라는 구조체에 담아 파라미터로 전달받는다.
  • GameplayAbility나 ASC의 함수에서 GameplayCue를 실행하고 싶다면,
    반드시 GameplayCueparameters를 채워서 GameplayCue에 전달해야만 한다.
  • 만약 GameplayCue가 GE로부터 발생했다면, FGameplayCueParameters에서 아래 변수들이 자동으로 채워진다.
    • AggregatedSourceTags
    • AggregatedTargetTags
    • GameplaEffectLevel
    • AbilityLevel
    • EffectContext
    • Magnitude
  • SourceObject 항목은 GameplayCue를 실행시킬 때 임시 데이터를 전달하기 적절한 변수이다.
  • Instigator와 같은 FGameplayCueParameters 안의 일부 변수들은 EffectContext에 이미 존재할 수 있다.
    • 또한 EffectContext는 GameplayCue가 World에서 Spawn 된 Location을 FHitResult로 가지고 있기도 하다.
  • 하지만 EffectContext를 확장할 때에는 GameplayCue에 더 많은 정보를 가지게 하는 것이 적절할 것이다.
    • 특히 GameplayCue가 GE에서 발생을 한다면 더더욱이.
  • UAbilitySystemGlobals에서는 FGameplayCueParameters를 채우는 함수들을 제공한다.
    • 이들은 virtual로 선언되어 있어 override를 통해 더 많은 정보를 자동으로 채워주도록 확장할 수 있다.
더보기
/** 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);

Gameplay Cue Manager

  • GameplayCueManager는 Default로 다음 기능이 동작한다.
    • GameplayCueNotifier에 대한 전체 Game Directory를 Scan
    • Scan 한 Directory들을 Play 할 때 Memory에 Load
  • 또한 defaultGame.ini에서 Scan 할 Game Directory를 변경할 수 있다.
더보기
[/Script/GameplayAbilities.AbilitySystemGlobals]
GameplayCueNotifyPaths="/Game/GASDocumentation/Characters"
  • 우리는 GameplayCueManager가 모든 GameplayCueNotifier를 Scan하기를 원한다.
    • 하지만 이들을 하나하나 Async Load 하는 것은 원하지 않는다.
    • GameplayCueManager는 Level에서 사용되는지 여부와 무관하게 모든 GameplayCueNotifier와
      관련 Sound, Particle을 Memory에 올려둘 것이다.
  • GameplayCue가 게임 시작 할 때 Async Load 하는 것에 대한 대안은 게임 중 실행 될 때 Async Load 하는 것이다.
    • 이는 Play 중 GameplayCue가 처음 호출 될 때 해당 Effect에 대해 약간의 Delay를 안고 가는 대신,
      불필요한 메모리 사용과 Async Load를 하는 동안 발생할 수 있는 Hard Freeze를 줄여준다.
    • Editor에서는 Particle System을 컴파일 해야 하는 경우 GameplayCue를 처음 Load 하는 동안
      약간의 히칭이나 Freeze가 발생할 수 있다.
  • 이러한 기능을 수행하기 위해, 가장 먼저 UGameplayCueManager를 확장해 아래와 같이 함수를 override 해야 한다.
더보기
virtual bool ShouldAsyncLoadRuntimeObjectLibraries() const override
{
	return false;
}
  • 그리고 확장한 Class Type을 DefaultGame.ini를 통해 AbilitySystemGlobals에 전달해야 한다.
더보기
[/Script/GameplayAbilities.AbilitySystemGlobals]
GlobalGameplayCueManagerClass="/Script/ParagonAssets.PBGameplayCueManager"

Prevent Gameplay Cue from Firing

  • 때로는 GameplayCue가 발생하는 것을 막아야 하는 경우가 있다.
    • 공격이 막혔다던가.
    • Damage GE에 연결된 Hit Impact를 재생하고 싶지 않다던가.
    • 별개의 Hit Impact를 재생하고 싶다던가.
  • 그럴 때에는 아래 함수를 호출하고, 명시적으로 GameplayCue Event를 Targer/Source ASC에 전달하면 된다.
더보기
void FGameplayEffectCustomExecutionOutput::MarkGameplayCuesHandledManually()
{
	bHandledGameplayCuesManually = true;
}
  • 만약 특정 ASC에서 영구적으로 GameplayCue가 발생하지 않기 원한다면, 아래 변수 값을 수정해주면 된다.
더보기
/** Suppress all GameplayCues on this component */
UPROPERTY()
bool bSuppressGameplayCues;

Batch Gameplay Cue

  • 각 Gameplay Cue는 unreliable NetMulticast RPC로 호출된다.
  • 만약 동시에 여러개의 Gameplay Cue가 발생 한다면, 몇 가지 최적화를 적용할 수 있다.
    • RPC를 하나로 합치던가
    • Data를 압축하여 더 적은 정보를 패킷으로 전송하던가.

Manual RPC

  • 예를 들어 산탄을 사격한다고 가정하자.
    • 산탄이 발사하면 pellet의 수 만큼 TargetData에 대한 EffectContext가 발생을 한다.
    • 이걸 1개로 합친다 하더라도 Data 용량은 1회 RPC 용량(500 byte)를 초과할 것이다.
  • 이를 좀 더 최적화 하는 방법은 별도의 구조체로 정보를 보내는 것이다.
    • 이 구조체는 Hit Location은 효과적으로 encode 하였거나,
      Client에서 Impact Location을 재생성/근사 할 수 있도록 Random Seed Number를 가지고 있을 것이다.
    • Client는 이 정보를 통해 Local Gameplay Cue를 발생시켜 작업을 복구할 것이다.
  • 이 방식은 보통 다음 조건에서 사용된다.
    • GameplayCue에서 필요로 하는 일부 parameter가 GameplayCueParameter에서 제공한 것과 맞지 않은 경우
    • GameplayCue에서 필요로 하는 일부 parameter를 EffectContext에 추가하고 싶지 않은 경우
      • DamageID, 치명타 계수, 실드 파괴 지수, 결정타 지수 등

사용 방법

  • FScopedGameplayCueSendContext를 선언한다.
    • 이 구조체는 Scope를 벗어날 때까지 UGameplayCueManager::FlushPendingCues() 함수를 억제한다.
    • 즉, 모든 GameplayCue는 이 구조체 Scope를 벗어날 때까지 queue에 쌓이게 된다.
더보기
/**
 *	FScopedGameplayCueSendContext
 *	Add this around code that sends multiple gameplay cues to allow grouping them into a smalkler number of cues for more efficient networking
 */
struct GAMEPLAYABILITIES_API FScopedGameplayCueSendContext
{
	FScopedGameplayCueSendContext();
	~FScopedGameplayCueSendContext();
};
  • FlushPendingCues() 함수를 override하여 Custom Struct에서
    몇몇 Custom Gamplay Tag로 batch 할수 있는 GameplayCue를 Merge하고, Client로 RPC 한다.
더보기
void UGameplayCueManager::FlushPendingCues()
{
	OnFlushPendingCues.Broadcast();

	TArray<FGameplayCuePendingExecute> LocalPendingExecuteCues = PendingExecuteCues;
	PendingExecuteCues.Empty();
	for (int32 i = 0; i < LocalPendingExecuteCues.Num(); i++)
	{
		FGameplayCuePendingExecute& PendingCue = LocalPendingExecuteCues[i];

		// Our component may have gone away
		if (PendingCue.OwningComponent)
		{
			bool bHasAuthority = PendingCue.OwningComponent->IsOwnerActorAuthoritative();
			bool bLocalPredictionKey = PendingCue.PredictionKey.IsLocalClientKey();

			IAbilitySystemReplicationProxyInterface* RepInterface = PendingCue.OwningComponent->GetReplicationInterface();
			if (RepInterface == nullptr)
			{
				// If this returns null, it means "we are replicating througha proxy and have no avatar". Which in this case, we should skip
				continue;
			}

			// TODO: Could implement non-rpc method for replicating if desired
			if (PendingCue.PayloadType == EGameplayCuePayloadType::CueParameters)
			{
				if (ensure(PendingCue.GameplayCueTags.Num() >= 1))
				{
					if (bHasAuthority)
					{
						RepInterface->ForceReplication();
						if (PendingCue.GameplayCueTags.Num() > 1)
						{
							RepInterface->Call_InvokeGameplayCuesExecuted_WithParams(FGameplayTagContainer::CreateFromArray(PendingCue.GameplayCueTags), PendingCue.PredictionKey, PendingCue.CueParameters);
						}
						else
						{
							RepInterface->Call_InvokeGameplayCueExecuted_WithParams(PendingCue.GameplayCueTags[0], PendingCue.PredictionKey, PendingCue.CueParameters);

							static FName NetMulticast_InvokeGameplayCueExecuted_WithParamsName = TEXT("NetMulticast_InvokeGameplayCueExecuted_WithParams");
							CheckForTooManyRPCs(NetMulticast_InvokeGameplayCueExecuted_WithParamsName, PendingCue, PendingCue.GameplayCueTags[0].ToString(), nullptr);
						}
					}
					else if (bLocalPredictionKey)
					{
						for (const FGameplayTag& Tag : PendingCue.GameplayCueTags)
						{
							PendingCue.OwningComponent->InvokeGameplayCueEvent(Tag, EGameplayCueEvent::Executed, PendingCue.CueParameters);
						}
					}
				}
			}
			else if (PendingCue.PayloadType == EGameplayCuePayloadType::FromSpec)
			{
				if (bHasAuthority)
				{
					RepInterface->ForceReplication();
					RepInterface->Call_InvokeGameplayCueExecuted_FromSpec(PendingCue.FromSpec, PendingCue.PredictionKey);

					static FName NetMulticast_InvokeGameplayCueExecuted_FromSpecName = TEXT("NetMulticast_InvokeGameplayCueExecuted_FromSpec");
					CheckForTooManyRPCs(NetMulticast_InvokeGameplayCueExecuted_FromSpecName, PendingCue, PendingCue.FromSpec.Def ? PendingCue.FromSpec.ToSimpleString() : TEXT("FromSpecWithNoDef"), PendingCue.FromSpec.EffectContext.Get());
				}
				else if (bLocalPredictionKey)
				{
					PendingCue.OwningComponent->InvokeGameplayCueEvent(PendingCue.FromSpec, EGameplayCueEvent::Executed);
				}
			}
		}
	}
}
  • Client는 Custom Struct를 받아 Local GameplayCue로 실행한다.

Multiple Gameplay Cue on One Gameplay Effect

  • GameplayEffect에서의 Gameplay Cue는 이미 하나의 RPC로 전달된다.
  • 아래 함수에서 모든 GameplayEffectSpec를 FGameplayEffectSpecForRPC로 변환하여
    ASC의 Replication Mode와 무관하게 unreliable NetMulticast로 전달한다.
더보기
virtual void InvokeGameplayCueAddedAndWhileActive_FromSpec(UAbilitySystemComponent* OwningComponent, const FGameplayEffectSpec& Spec, FPredictionKey PredictionKey);

 

  • 하지만 이 방식은 GameplayEffectSpec에 어떤 정보가 있는지에 따라 매우 큰 Bandwidth를 가질 수 있다.
    • 여기에 대해 아래 변수를 통해 좀 더 기능을 최적화 할 수 있다.
더보기
/**
 *	Enabling AbilitySystemAlwaysConvertGESpecToGCParams will mean that all calls to gameplay cues with GameplayEffectSpecs will be converted into GameplayCue Parameters server side and then replicated.
 *	This potentially saved bandwidth but also has less information, depending on how the GESpec is converted to GC Parameters and what your GC's need to know.
 */

int32 AbilitySystemAlwaysConvertGESpecToGCParams = 0;
static FAutoConsoleVariableRef CVarAbilitySystemAlwaysConvertGESpecToGCParams(TEXT("AbilitySystem.AlwaysConvertGESpecToGCParams"), AbilitySystemAlwaysConvertGESpecToGCParams, TEXT("Always convert a GameplayCue from GE Spec to GC from GC Parameters on the server"), ECVF_Default );
  • 이 변수는 FGameplayEffectSpec를 FGameplayCueParameter로 변환하여 RPC에 전달한다.
  • Bandwidth를 절약할 수 있는 대신 더 적은 정보를 담을 수 밖에 없다.
    • 때문에 GESpec을 GameplayCueParameter로 어떻게 변환할지가 중요하다.

Gameplay Cue Event

더보기
UENUM(BlueprintType)
namespace EGameplayCueEvent
{
	/** Indicates what type of action happened to a specific gameplay cue tag. Sometimes you will get multiple events at once */
	enum Type : int
	{
		/** Called when a GameplayCue with duration is first activated, this will only be called if the client witnessed the activation */
		OnActive,

		/** Called when a GameplayCue with duration is first seen as active, even if it wasn't actually just applied (Join in progress, etc) */
		WhileActive,

		/** Called when a GameplayCue is executed, this is used for instant effects or periodic ticks */
		Executed,

		/** Called when a GameplayCue with duration is removed */
		Removed
	};
}

OnActive

  • GameplayCue가 활성화(Add) 될 때 호출
  • GameplayCue가 시작할 때 발생하는 모든 것에서 사용
    • 단, Joiner가 늦어서 이 Event를 놓치더라도 WhileActive에서 보강해줄 수 있다.

WhileActive

  • GameplayCue가 동작 할 때 호출된다.
    • 심지어 아직 등록이 완료되지 않더라도 동작만 하면 호출된다.
  • GameplayCueNotify_Actor가 Add 되거나 관련 작업이 생기면 OnActive와 같이 한번만 호출된다.
    • 따라서 Tick이 아니다.
    • Actor가 GameplayCueNotify_Actor 범위 안에 들어올 때마다 한번씩 발생한다.
  • 만약 Tick이 필요하다면, GameplayCueNotify_Actor::Tick()을 사용해야 한다.

Removed

  • GameplayCue가 제거 될 때 호출된다.
    • BP에서는 OnRemove Event가 이 기능과 관련 있다.
  • OnActive/WhileActive에서 추가한 것들을 반드시지워야 한다.
  • Actor가 GameplayCueNotify_Actor 범위 밖을 나갈 때마다 한번씩 발생한다.

Executed

  • Instant Effect나 Periodic Tick에서 GameplayCue가 실해오딜 때 호출된다.
    • BP에서는 OnExecute가 관련있다.

Gameplay Cue Reliability

  • 일반적으로 GameplayCue는 Unreliable 함을 염두해야만 한다.
    • 때문에 Gameplay에 직접적으로 영향을 주는 모든 겨우에 대해서는 적합하지 않다.
  • 만약 GameplayCue를 Relaible하게 사용하고 싶다면, 
    GameplayEffect으로부터 등록하여 WhileActive에서 FX를 추가하고, OnRevmoe에서 제거해야 한다.

Executed GameplayCues

  • 이 경우에는 Unreliable Multicast로 등록이 되기 때문에 항상 Unreliable하다.

GameplayCues applied from GameplayEffect

  • Autonomous Proxy의 경우 OnActive, WhileActive, OnRemove에 대해 Reliable하다.
    • OnActive, WhileActive는 다음 함수에서 호출된다.
    • 더보기
      void FActiveGameplayEffectsContainer::PostReplicatedReceive(const FFastArraySerializer::FPostReplicatedReceiveParameters& Parameters)
      {
      	// After the array has been replicated, invoke GC events ONLY if the effect is not inhibited
      	// We postpone this check because in the same net update we could receive multiple GEs that affect if one another is inhibited	
      	if (Owner != nullptr)
      	{
      		QUICK_SCOPE_CYCLE_COUNTER(STAT_ActiveGameplayEffectsContainer_NetDeltaSerialize_CheckRepGameplayCues);
      
      		if ( LIKELY(UE::GameplayEffect::bSkipUnmappedReferencesCheckForGameplayCues) )
      		{
      			if (Owner->IsReadyForGameplayCues())
      			{
      				Owner->HandleDeferredGameplayCues(this);
      			}
      		}
      		else
      		{
      // Keep this until we have actually deprecated the parameter just in case.
      PRAGMA_DISABLE_DEPRECATION_WARNINGS
      			if (!Parameters.bHasMoreUnmappedReferences) // Do not invoke GCs when we have missing information (like AActor*s in EffectContext)
      			{
      				NumConsecutiveUnmappedReferencesDebug = 0;
      				if (Owner->IsReadyForGameplayCues())
      				{
      					Owner->HandleDeferredGameplayCues(this);
      				}
      			}
      			else
      			{
      				++NumConsecutiveUnmappedReferencesDebug;
      
      				constexpr uint32 HighNumberOfConsecutiveUnmappedRefs = 30;
      				ensureMsgf(NumConsecutiveUnmappedReferencesDebug < HighNumberOfConsecutiveUnmappedRefs, TEXT("%hs: bHasMoreUnmappedReferences is preventing GameplayCues from firing"), __func__);
      				UE_CLOG((NumConsecutiveUnmappedReferencesDebug % HighNumberOfConsecutiveUnmappedRefs) == 0, LogAbilitySystem, Error, TEXT("%hs: bHasMoreUnmappedReferences is preventing GameplayCues from firing (%u consecutive misses)"), __func__, NumConsecutiveUnmappedReferencesDebug);
      			}
      PRAGMA_ENABLE_DEPRECATION_WARNINGS
      		}
      	}
      }

       

      void UAbilitySystemComponent::HandleDeferredGameplayCues(const FActiveGameplayEffectsContainer* GameplayEffectsContainer)
      {
      	for (const FActiveGameplayEffect& Effect : GameplayEffectsContainer)
      	{
      		if (Effect.bIsInhibited == false)
      		{
      			if (Effect.bPendingRepOnActiveGC)
      			{
      				InvokeGameplayCueEvent(Effect.Spec, EGameplayCueEvent::OnActive);
      			}
      			if (Effect.bPendingRepWhileActiveGC)
      			{
      				InvokeGameplayCueEvent(Effect.Spec, EGameplayCueEvent::WhileActive);
      			}
      		}
      
      		Effect.bPendingRepOnActiveGC = false;
      		Effect.bPendingRepWhileActiveGC = false;
      	}
      }


    • OnRemoved의 경우 다음 함수에서 호출된다.
    • 더보기
      void FActiveGameplayEffectsContainer::RemoveActiveGameplayEffectGrantedTagsAndModifiers(const FActiveGameplayEffect& Effect, bool bInvokeGameplayCueEvents)
      {
      	// Update AttributeAggregators: remove mods from this ActiveGE Handle
      	if (Effect.Spec.GetPeriod() <= UGameplayEffect::NO_PERIOD)
      	{
      		for (const FGameplayModifierInfo& Mod : Effect.Spec.Def->Modifiers)
      		{
      			if (Mod.Attribute.IsValid())
      			{
      				if (const FAggregatorRef* RefPtr = AttributeAggregatorMap.Find(Mod.Attribute))
      				{
      					RefPtr->Get()->RemoveAggregatorMod(Effect.Handle);
      				}
      			}
      		}
      	}
      
      	// Update gameplaytag count and broadcast delegate if we are at 0
      	Owner->UpdateTagMap(Effect.Spec.Def->GetGrantedTags(), -1);
      	Owner->UpdateTagMap(Effect.Spec.DynamicGrantedTags, -1);
      
      	// Update our owner with the blocked ability tags this GameplayEffect adds to them
      	Owner->UnBlockAbilitiesWithTags(Effect.Spec.Def->GetBlockedAbilityTags());
      
      	// Update minimal replication if needed.
      	if (ShouldUseMinimalReplication())
      	{
      		Owner->RemoveMinimalReplicationGameplayTags(Effect.Spec.Def->GetGrantedTags());
      		Owner->RemoveMinimalReplicationGameplayTags(Effect.Spec.DynamicGrantedTags);
      	}
      
      PRAGMA_DISABLE_DEPRECATION_WARNINGS
      	// Cancel/remove granted abilities
      	if (IsNetAuthority())
      	{
      		for (const FGameplayAbilitySpecDef& AbilitySpecDef : Effect.Spec.GrantedAbilitySpecs)
      		{
      			if (AbilitySpecDef.AssignedHandle.IsValid())
      			{
      				switch(AbilitySpecDef.RemovalPolicy)
      				{
      				case EGameplayEffectGrantedAbilityRemovePolicy::CancelAbilityImmediately:
      					{
      						Owner->ClearAbility(AbilitySpecDef.AssignedHandle);
      						break;
      					}
      				case EGameplayEffectGrantedAbilityRemovePolicy::RemoveAbilityOnEnd:
      					{
      						Owner->SetRemoveAbilityOnEnd(AbilitySpecDef.AssignedHandle);
      						break;
      					}
      				default:
      					{
      						// Do nothing to granted ability
      						break;
      					}
      				}
      			}
      		}
      	}
      PRAGMA_ENABLE_DEPRECATION_WARNINGS
      
      	// Update GameplayCue tags and events
      	if (!Owner->bSuppressGameplayCues)
      	{
      		for (const FGameplayEffectCue& Cue : Effect.Spec.Def->GameplayCues)
      		{
      			// If we use Minimal/Mixed Replication, then this will cause EGameplayCueEvent::Removed
      			if (ShouldUseMinimalReplication())
      			{
      				for (const FGameplayTag& CueTag : Cue.GameplayCueTags)
      				{
      					Owner->RemoveGameplayCue_MinimalReplication(CueTag);
      				}
      			}
      			else
      			{
      				// Perform pseudo-RemoveCue (without affecting ActiveGameplayCues, as we were not inserted there - see AddActiveGameplayEffectGrantedTagsAndModifiers)
      				Owner->UpdateTagMap(Cue.GameplayCueTags, -1);
      
      				if (bInvokeGameplayCueEvents)
      				{
      					Owner->InvokeGameplayCueEvent(Effect.Spec, EGameplayCueEvent::Removed);
      				}
      			}
      		}
      	}
      }
  • Simulated Proxy의 경우 WhileActive와 OnRemove가 Reliable하다.
    • 아래 함수에서의 Replication이 WhileActive, OnRemove Event를 호출한다.
    • 더보기
      /** Replicated gameplaycues when in minimal replication mode. These are cues that would come normally come from ActiveGameplayEffects (but since we do not replicate AGE in minimal mode, they must be replicated through here) */
      UPROPERTY(Replicated)
      FActiveGameplayCueContainer MinimalReplicationGameplayCues;

       

    • OnActive Event는 Unreliable Multicast로 전달된다.

GameplayCues applied withou a GameplayEffect 

  • Autonomous Proxy의 경우 OnRemove Event를 Reliable하게 받는다.
    • OnActive, WhileActive Event는 Unreliable Multicast로 전달된다.
  • Simulated Proxy의 경우 WhileActive, OnRemove Event를 Reliable하게 받는다.
    • UASC::MinimalReplicationGameplayCue의 Replication이 WhileActive, OnRemove를 호출한다.
    • OnActive Event는 Unreliable Multicast로 전달된다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Prediction  (0) 2024.05.21
[GAS] Gameplay Ability  (0) 2024.05.17
[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/using-gameplay-abilities-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 사용하기 | 언리얼 엔진 5.4 문서 | Epic Developer Community

게임플레이 어빌리티 클래스의 개요입니다.

dev.epicgames.com

 

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#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. - tranek/GASDocumentation

github.com

 

  • Game 내 Actor가 하는 모든 행동이나 Skill, Ability가 하는 일, Cost, 조건 및 시점을 정의
  • UGameplayAbility를 상속
  • GA는 비동기로 실행되는 Instance화 된 Object로 존재할 수 있다.
    • 다양한 캐릭터 상호작용에 따른 분기처럼 전문화 된 다단계 Task를 실행할 수 있다.
    • Character Animation, Particle/Sound Effect, Player Input 이나 실행 등
  • Net Execution Policy를 통한 자체 Replicate, Client/Server에서의 실행, 변수 동기화나 RPC 호출도 가능
    • 단, Simulated Proxy에는 호출 불가
  • Game Session 중에 Engine이 GA를 구현하는 방식에 있어 유연성을 제공합니다.
    • Cooldown, Cost, Player Input, Anim Montage Animation, Actor에 부여되는
      Ability 자체에 대한 반응을 구현하는 확장 함수
  • 여러 시간에 걸쳐서 발생하는 Gameplay Ability를 실행하는 데에 Ability Task를 사용
    • Attribute 변경 대기
    • Target 선택 대기
    • Root Motion Source의 캐릭터 움직임 등
  • Simulated client는 Gameplay Ability를 호출하지 않는다.
    • 대신 서버에서 Ability가 동작하고, 시각적으로 재생이 필요한 것들은 모두
      Ability Task나 GameplayCue를 통해 Replicate 된다.
  • BP, C++에서 모두 작업 가능

Bind Input to ASC

  • ASC는 직접적인 Input Bind를 제공하고 이에 대해 GameAbility를 Grant 할 수 있도록 기능을 제공한다.
  • GameAbility에 등록된 Input Action은 Press가 발생할 때 Gameplay Tag 조건을 만족하면
    자동으로 Gameplay Activity로 동작한다.
  • Input Action이 등록된 Input에 반응하려면 내장된 Ability Task를 요구한다.
  • 활성화 된 Gameplay Ability에 등록된 Input Action에 더불어, ASC도 범용적인 Confirm, Cancel Input을 받을 수 있다.
    • 이런 특별한 Input은 Ability Task에서 Target Actor나 Ability 취소와 같은 것들을 등록할 때 사용된다.
  • ASC에 Input을 Bind하려면 가장 먼저 Inpuat Action Name을 byte로 변환하는 Enum을 선언해야 한다.
    • 이 Enum의 이름은 Project Setting에서의 Input Action 이름과 완벽하게 일치해야 한다.
    • DisplayName은 아무 상관 없다.
더보기
UENUM(BlueprintType)
enum class EGDAbilityInputID : uint8
{
	// 0 None
	None			UMETA(DisplayName = "None"),
	// 1 Confirm
	Confirm			UMETA(DisplayName = "Confirm"),
	// 2 Cancel
	Cancel			UMETA(DisplayName = "Cancel"),
	// 3 LMB
	Ability1		UMETA(DisplayName = "Ability1"),
	// 4 RMB
	Ability2		UMETA(DisplayName = "Ability2"),
	// 5 Q
	Ability3		UMETA(DisplayName = "Ability3"),
	// 6 E
	Ability4		UMETA(DisplayName = "Ability4"),
	// 7 R
	Ability5		UMETA(DisplayName = "Ability5"),
	// 8 Sprint
	Sprint			UMETA(DisplayName = "Sprint"),
	// 9 Jump
	Jump			UMETA(DisplayName = "Jump")
};

ASC in Character

  • ACharacter::SetupPlayerInputcomponent() 함수안에서 ASC에 Bind 하는 작업을 추가
더보기
/** Bind to an input component with customized bindings */
virtual void BindAbilityActivationToInputComponent(UInputComponent* InputComponent, FGameplayAbilityInputBinds BindInfo);

void ACharacter::SetupPlayerInputComponent
{
    // Bind to AbilitySystemComponent
    FTopLevelAssetPath AbilityEnumAssetPath = FTopLevelAssetPath(FName("/Script/GASDocumentation"), FName("EGDAbilityInputID"));
    AbilitySystemComponent->BindAbilityActivationToInputComponent(PlayerInputComponent, FGameplayAbilityInputBinds(FString("ConfirmTarget"),
	FString("CancelTarget"), AbilityEnumAssetPath, static_cast<int32>(EGDAbilityInputID::Confirm), static_cast<int32>(EGDAbilityInputID::Cancel)));
}

ASC in PlayerState

  • PlayerState가 아직 Client에 Replicate 되지 않을 수 있어 ACharacter::SetupPlayerInputComponent 안에서
    Race Condition이 발생할 위험이 있다.
  • 때문에 ACharacter::SetupPlayerInputcomponent 뿐 아니라 OnRep_PlayerState() 안에서도 Bind 작업이 필요하다.
  • OnRep_PlayerState() 자체만으로는 Bind 작업에 충분치 않다.
    • PlayerController가 Cliient에서 ClientRestart()를 호출해 InputComponent를 생성하기 전에
      PlayerState가 Replicate 되면 Actor의 InputComponent가 null이 될 수 있기 때문이다.

Bind to Input Without Actiavting Ability

  • Input이 발생할 때 Gameplay Ability가 동작하지는 않지만, Ability Task를 통한 Bind는 계속 유지하고 싶을 때 사용
  • UAbilitySystemComponent::AbilityLocalInputPressed()를 override 하여 Gameplay Ability 작업을 Customize.

Basic Usage

Activation Sequence

Local pretiction

  • TryActivateAbility()
  • InternalTryActivateAbility()
  • CanActivateAbility()
    • Gameplay Tag 조건이 부합한디
    • ASC에서 Cost를 지불할 수 있는지
    • Gameplay Ability가 Cooldown 상태가 아닌지
    • 이미 활성화된 Instance가 없는지
  • CallServerTryActivateAbility()
    • 이 함수를 통하 생성된 Pretiction Key를 전달한다.
  • CallActivateAbility()
  • PreActivate()
    • Epic은 이걸 Boilerplate Init Stuff라 부른다.
  • ActivateAbility()
    • 이 함수에서 비로소 Ability가 동작한다.

Server

  • CallServerTryActivateAbility()에서 RPC를 받으면서 시작
  • ServerTryActivateAbility()
  • InterServerTryActivateAbility()
  • InternalTryActivateAbility()
  • CanActivateAbility()
    • Local Pretiction과 동일한 조건을 체크, 반환
  • ClientActivateAbilitySucced()
    • 다음 두 조건을 만족 하면 호출 됨
      • Server에서 활성화 되는 ActivationInfo가 성공적으로 수정 된 경우.
      • OnConfirmDelegate가 Broadcast 된 경우
  • CallActivateAbility()
  • PreActivate()
  • ActivateAbility()
    • 여기서 Ability가 동작한다.
  • ClientActivateAbilityFailed()
    • Server가 Ability 활성화에 실패한 경우에 호출 된다.
    • 즉시 Client의 Gameplay Ability를 제거하고 적용된 것들을 복구한다.

Grant Ability

  • ASC의 ActivatableAbility에 Gameplay Ability를 추가
    • ActivatableAbility에서 Gameplay Tag 조건이 맞으면 활성화가 된다.
  • Gameplay Ability를 서버에서 등록하면 GameplayAbilitySpec을 소유한 Client에 자동으로 Replicate 한다.
    • 다른 Client나 Simulated Proxy는 GameplayAbilitySpec을 받지 않는다.
    • GameplayAbilitySpec은 Gameplay Ability가 Grant 될 때 생성
    • Ability Level, Bind 된 input, 해당 Gameplay Ability를 ASC에 전달한 Source Object 정보를 담고 있다.

GiveAbility

더보기
/*
 * Grants an Ability.
 * This will be ignored if the actor is not authoritative.
 * Returns handle that can be used in TryActivateAbility, etc.
 * 
 * @param AbilitySpec FGameplayAbilitySpec containing information about the ability class, level and input ID to bind it to.
 */
FGameplayAbilitySpecHandle GiveAbility(const FGameplayAbilitySpec& AbilitySpec);

 

void AGDCharacterBase::AddCharacterAbilities()
{
	// Grant abilities, but only on the server	
	if (Role != ROLE_Authority || !AbilitySystemComponent.IsValid() || AbilitySystemComponent->bCharacterAbilitiesGiven)
	{
		return;
	}

	for (TSubclassOf<UGDGameplayAbility>& StartupAbility : CharacterAbilities)
	{
		AbilitySystemComponent->GiveAbility(
			FGameplayAbilitySpec(StartupAbility, GetAbilityLevel(StartupAbility.GetDefaultObject()->AbilityID), static_cast<int32>(StartupAbility.GetDefaultObject()->AbilityInputID), this));
	}

	AbilitySystemComponent->bCharacterAbilitiesGiven = true;
}
  • FGameplayAbilitySpec으로 추가할 Ability를 나타냄
  • FGameplayAbilitySpecHandle을 반환

GiveAbilityAndActivateOnce

더보기
/*
 * Grants an ability and attempts to activate it exactly one time, which will cause it to be removed.
 * Only valid on the server, and the ability's Net Execution Policy cannot be set to Local or Local Predicted
 * 
 * @param AbilitySpec FGameplayAbilitySpec containing information about the ability class, level and input ID to bind it to.
 * @param GameplayEventData Optional activation event data. If provided, Activate Ability From Event will be called instead of ActivateAbility, passing the Event Data
 */
FGameplayAbilitySpecHandle GiveAbilityAndActivateOnce(FGameplayAbilitySpec& AbilitySpec, const FGameplayEventData* GameplayEventData = nullptr);
  • FGameplayAbilitySpec으로 추가할 Ability를 나타냄
  • FGameplayAbilitySpecHandle을 반환
  • Ability는 반드시 Instance화되고 Server에서 실행할 수 있어야 한다.
    • Server에서 실행을 시도한 후에는 FGameplayAbilitySpecHandle을 반환한다.
  • Ability가 필수 조건을 충족하지 못하거나 실행 할 수 없는 경우,
    Handle이 유효하지 않아 ASC가 Ability를 부여받지 못한다.

Revoke Ability

  • 입력받은 FGameplayAbilitySpecHandle은 모두 Grant 과정에서 반환한 것들을 사용한다.

ClearAbility

더보기
/** 
 * Removes the specified ability.
 * This will be ignored if the actor is not authoritative.
 * 
 * @param Handle Ability Spec Handle of the ability we want to remove
 */
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities")
void ClearAbility(const FGameplayAbilitySpecHandle& Handle);
  • 지정한 Ability를 ASC에서 제거한다.

ClearAllAbilities

더보기
/** Wipes all 'given' abilities. This will be ignored if the actor is not authoritative. */
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category="Gameplay Abilities")
void ClearAllAbilities();
  • ASC의 모든 Ability를 제거한다.

ClearAllAbilitiesWithInputID

더보기
/**
 * Clears all abilities bound to a given Input ID
 * This will be ignored if the actor is not authoritative
 *
 * @param InputID The numeric Input ID of the abilities to remove
 */
UFUNCTION(BlueprintCallable, BlueprintAuthorityOnly, Category = "Gameplay Abilities")
void ClearAllAbilitiesWithInputID(int32 InputID = 0);
  • Input ID에 Bind 된 모든 Ability를 ASC에서 제거한다.

SetRemoveAbilityOnEnd

더보기
/** Sets an ability spec to remove when its finished. If the spec is not currently active, it terminates it immediately. Also clears InputID of the Spec. */
void SetRemoveAbilityOnEnd(FGameplayAbilitySpecHandle AbilitySpecHandle);
  • 지정한 Ability 실행이 완료되면 ASC에서 제거한다.
    • 실행중이지 않는 경우 즉시 제거한다.
    • 실행중인 경우 그 입력을 즉시 지워 Player가 더이상 재활성화/상호작용 못하도록 막는다.

CanActivateAbility

  • 호출자가 Ability 실행을 시도하지 않아도 사용 가능 여부를 알려주는 함수
    • UI에서 Player가 사용할 수 없는 Icon을 Dimd 처리해야 하는 경우
    • Character에 특정 Particle/Sound Effect를 재생해 특정 Ability가 사용 가능한지 Notify해야 하는 경우
더보기
/** Returns true if this ability can be activated right now. Has no side effects */
virtual bool CanActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayTagContainer* SourceTags = nullptr, const FGameplayTagContainer* TargetTags = nullptr, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const;

CallActivateAbility

  • Ability에 관련된 Game Code를 실행
    • 하지만 사용 가능 여부를 검사하지 않는다.
  • CanActivateAbility 검사와 Ability 실행 사이에 약간의 로직이 필요한 경우에 실행
  • Actor/Component와 달리 주 작업이 Tick을 수행하지 않는다.
    • 대신 활성화 중 비동기 작업을 시행할 수 있는 Ability Task를 지원 함.
    • C++에서는 Delegate로, BP에서는 실행 핀 노드로 해당 Task 출력을 처리한다.
더보기
/** Executes PreActivate and ActivateAbility */
void CallActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, FOnGameplayAbilityEnded::FDelegate* OnGameplayAbilityEndedDelegate = nullptr, const FGameplayEventData* TriggerEventData = nullptr);

ActivateAbility

  • 사용자가 Ability의 Custom Function 기능으로 Override해야 하는 Main Code
더보기
/** Actually activate ability, do not call this directly */
virtual void ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData);

CommitAbility

  • Activate 내에서 호출 한 경우 Ability 실행 비용을 적용하는 함수
    • Attribute에서 Game System에 맞는 Resource를 감산하고 쿨다운 적용
더보기
virtual bool CommitAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr);
virtual bool CommitAbilityCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const bool ForceCooldown, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr);
virtual bool CommitAbilityCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr);

CancelAbility

  • Ability 취소 매커니즘을 제공한다.
    • Ability는 CanBeCanceled 함수가 요청을 거부할 수 있다.
  • CommitAbility와 달리 Ability 자체 외부 호출자에서 사용할 수 있다.
  • 취소가 성공하면 다음 작업이 이어진다.
    • OnGameplayAbilityCancelled로 Broadcast
    • 해당 Ability를 종료하기 위한 표준 Code 경로로 이동
    • Ability에 특수한 Cleanup Code를 실행할 기회를 주거나,
      정상적으로 종료했을 때와 다른 동작을 하도록 함.
  • RemoteEndOrCancelAbility()에서 WasCancelled 파라미터를 true로 설정하면 내부에서 CalcelAbility가 호출된다.
더보기
/** Cancels the specified ability CDO. */
void CancelAbility(UGameplayAbility* Ability);	

/** Cancels the ability indicated by passed in spec handle. If handle is not found among reactivated abilities nothing happens. */
void CancelAbilityHandle(const FGameplayAbilitySpecHandle& AbilityHandle);

/** Cancel all abilities with the specified tags. Will not cancel the Ignore instance */
void CancelAbilities(const FGameplayTagContainer* WithTags=nullptr, const FGameplayTagContainer* WithoutTags=nullptr, UGameplayAbility* Ignore=nullptr);

/** Cancels all abilities regardless of tags. Will not cancel the ignore instance */
void CancelAllAbilities(UGameplayAbility* Ignore=nullptr);

/** Cancels all abilities and kills any remaining instanced abilities */
virtual void DestroyActiveState();

TryActivateAbility

  • Ability를 실행하는 전형적인 방식
  • CanActivateAbility를 호출해 실행 여부를 판단한 뒤, 가능하면 CallActivateAbility를 호출
더보기
/** 
 * Attempts to activate every gameplay ability that matches the given tag and DoesAbilitySatisfyTagRequirements().
 * Returns true if anything attempts to activate. Can activate more than one ability and the ability may fail later.
 * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate abilities.
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbilitiesByTag(const FGameplayTagContainer& GameplayTagContainer, bool bAllowRemoteActivation = true);

/**
 * Attempts to activate the ability that is passed in. This will check costs and requirements before doing so.
 * Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
 * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbilityByClass(TSubclassOf<UGameplayAbility> InAbilityToActivate, bool bAllowRemoteActivation = true);

/** 
 * Attempts to activate the given ability, will check costs and requirements before doing so.
 * Returns true if it thinks it activated, but it may return false positives due to failure later in activation.
 * If bAllowRemoteActivation is true, it will remotely activate local/server abilities, if false it will only try to locally activate the ability
 */
UFUNCTION(BlueprintCallable, Category = "Abilities")
bool TryActivateAbility(FGameplayAbilitySpecHandle AbilityToActivate, bool bAllowRemoteActivation = true);

/** Triggers an ability from a gameplay event, will only trigger on local/server depending on execution flags */
bool TriggerAbilityFromGameplayEvent(FGameplayAbilitySpecHandle AbilityToTrigger, FGameplayAbilityActorInfo* ActorInfo, FGameplayTag Tag, const FGameplayEventData* Payload, UAbilitySystemComponent& Component);
  • TriggetAbilityFromGameplayEvent를 사용하려면 해당 Gameplay Ability에 Trigger가 세팅되어 있어야 한다.
    • Gameplay Tag를 할당하고 Gameplay Event 옵션을 활성화 해야 한다.
    • Event를 사용하면 Data를 포함한 Payload에 전달할 수 있다.
  • GameplayTag가 추가/제거 되었을 때 GameplayAbility 활성화를 할 수 있다.
  • Event를 발생하려면 다음 함수를 사용해야 한다.
더보기
//UAbilitySystemBlueprintLibrary
/**
 * This function can be used to trigger an ability on the actor in question with useful payload data.
 * NOTE: GetAbilitySystemComponent is called on the actor to find a good component, and if the component isn't
 * found, the event will not be sent.
 */
UFUNCTION(BlueprintCallable, Category = Ability, Meta = (Tooltip = "This function can be used to trigger an ability on the actor in question with useful payload data."))
static void SendGameplayEventToActor(AActor* Actor, FGameplayTag EventTag, FGameplayEventData Payload);

Passive Ability

  • 자동으로 활성화 되고 계속해서 동작해야 하는 Gameplay Ability
  • UGameplayAbility::OnAvatarSet() 함수를 Override
    • Gameplay Ability가 등록되고 AvatarActor가 세팅되어 TryActivateAbility()가 호출 될 때 자동으로 호출된다.
  • Passive Ability는 기본적으로 Server Only로 동작한다.
더보기
void UGDGameplayAbility::OnAvatarSet(const FGameplayAbilityActorInfo * ActorInfo, const FGameplayAbilitySpec & Spec)
{
	Super::OnAvatarSet(ActorInfo, Spec);

	if (bActivateAbilityOnGranted)
	{
		ActorInfo->AbilitySystemComponent->TryActivateAbility(Spec.Handle, false);
	}
}

Activation Failed Tag

  • Ability는 왜 활성화에 실패했는지 알려주는 기능을 제공하고 있다.
  • 이 기능을 사용하려면 기본적인 실패 케이스에 해당하는 GameplayTag를 세팅하고,
    [Project]/Config/DefaultGame.ini 파일에 등록해야 한다,
  • Project
더보기
+GameplayTagList=(Tag="Activation.Fail.BlockedByTags",DevComment="")
+GameplayTagList=(Tag="Activation.Fail.CantAffordCost",DevComment="")
+GameplayTagList=(Tag="Activation.Fail.IsDead",DevComment="")
+GameplayTagList=(Tag="Activation.Fail.MissingTags",DevComment="")
+GameplayTagList=(Tag="Activation.Fail.Networking",DevComment="")
+GameplayTagList=(Tag="Activation.Fail.OnCooldown",DevComment="")
  • DefaultGame.ini
더보기
[/Script/GameplayAbilities.AbilitySystemGlobals]
ActivateFailIsDeadName=Activation.Fail.IsDead
ActivateFailCooldownName=Activation.Fail.OnCooldown
ActivateFailCostName=Activation.Fail.CantAffordCost
ActivateFailTagsBlockedName=Activation.Fail.BlockedByTags
ActivateFailTagsMissingName=Activation.Fail.MissingTags
ActivateFailNetworkingName=Activation.Fail.Networking

EndAbility

  • Ability 실행을 마치면 Ability를 종료
  • Ability가 취소 된 경우 UGameplayAbility 클래스에서 취소 Process의 일부로 자동 처리를 한다.
    • 하지만 그 외 경우에는 개발자가 C+++ 함수를 호출하거나 BPNode를 추가해야 한다.
  • Ability를 정상적으로 종료하지 못하면 GAS는 아직 실행중으로 판단,
    이후 해당 Ability는 이를 차단하는 다른 Ability를 사용하지 못하게 할 수 있다.
더보기
/** Called by ServerEndAbility and ClientEndAbility; avoids code duplication. */
void	RemoteEndOrCancelAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo, bool bWasCanceled);

UFUNCTION(Server, reliable, WithValidation)
void	ServerEndAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo, FPredictionKey PredictionKey);

UFUNCTION(Client, reliable)
void	ClientEndAbility(FGameplayAbilitySpecHandle AbilityToEnd, FGameplayAbilityActivationInfo ActivationInfo);

Get ActiveAbilities

  • Active Ability란 존재할 수 없다.
    • 한번에 여러개의 Gameplay Ability가 활성화 될 수 있기 때문이다.
  • 대신 ASC::ActivatableAbilities에서 Asset/Granted/GameplayTag가 일치한 Ability를 검색해야만 한다.
    • GetActivatableAbilities() 함수를 통해 ActivatableAbilities의 TArray<FGameplayAbilitySpec>를 제공한다.
더보기
/** Returns the list of all activatable abilities. Read-only. */
const TArray<FGameplayAbilitySpec>& GetActivatableAbilities() const
{
	return ActivatableAbilities.Items;
}

/** Returns the list of all activatable abilities. */
TArray<FGameplayAbilitySpec>& GetActivatableAbilities()
{
	return ActivatableAbilities.Items;
}
  • 또한 GameplayAbilitySpecs를 수동으로 순회하는 대신 편의 함수를 제공한다.
더보기
/** 
 * Gets all Activatable Gameplay Abilities that match all tags in GameplayTagContainer AND for which
 * DoesAbilitySatisfyTagRequirements() is true.  The latter requirement allows this function to find the correct
 * ability without requiring advanced knowledge.  For example, if there are two "Melee" abilities, one of which
 * requires a weapon and one of which requires being unarmed, then those abilities can use Blocking and Required
 * tags to determine when they can fire.  Using the Satisfying Tags requirements simplifies a lot of usage cases.
 * For example, Behavior Trees can use various decorators to test an ability fetched using this mechanism as well
 * as the Task to execute the ability without needing to know that there even is more than one such ability.
 */
void GetActivatableGameplayAbilitySpecsByAllMatchingTags(const FGameplayTagContainer& GameplayTagContainer, TArray < struct FGameplayAbilitySpec* >& MatchingGameplayAbilities, bool bOnlyAbilitiesThatSatisfyTagRequirements = true) const;
  • bOnlyAbilitiesThatSatisfyTagRequirements
    • 주어진 Gameplay Tag 조건을 만족하고 현재 활성화 되어 있는 GameplayAbilitySpec만 반환할지 여부
  • 한번 FGameplayAbilitySpec을 탐색하면 IsActive()를 호출할 수 있다.

Tags

  • 여러 Gameplay Ability의 상호작용 방식을 결정

Tag Set

  • Ability의 Behaviour에 영향을 미칠수 있는 방식
  • Ability를 식별 및 분류

Gamplay Tag Container

  • 다른 Ability와의 Interaction을 지원

Cancel Abilities With Tag

  • 이 Ability가 실행되는 동안 이미 실행 중인 Ability의 Tag가 제공된 목록과 일치하면 그 Ability를 취소한다.

Block Abilities With Tag

  • 이 Ability가 실행되는 동안 일치하는 Tag가 있는 다른 Ability의 실행을 방지한다.

Activation Owned Tags

  • 이 Ability가 실행되는 동안 해당 Ability의 Owner에 이 Tag Set가 부여된다.

Activation Required Tags

  • 이 Ability는 Active 상태인 Actor/Component에 모든 Tag가 있을 때에만 Activate 할 수 있다.

Activation Blocked Tags

  • 이 Ability는 Active 상태인 Actor/Component에 모든 Tag가 없을 때에만 Activate 할 수 있다.

Target Required Tags

  • 이 Ability는 Target Actor/Component에 이 Tag가 모두 있을 때에만 Activate 할 수 있다.

Target Blocked Tags

  • 이 Ability는 Target Actor/Component에 이 Tag가 모두 없을 때에만 Actiavte 할 수 있다.

Gameplay Tag Query

  • 다른 Ability와의 Interaction을 지원

Gameplay Ability Spec

더보기
/**
 * An activatable ability spec, hosted on the ability system component. This defines both what the ability is (what class, what level, input binding etc)
 * and also holds runtime state that must be kept outside of the ability being instanced/activated.
 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayAbilitySpec : public FFastArraySerializerItem
{
	GENERATED_USTRUCT_BODY()

PRAGMA_DISABLE_DEPRECATION_WARNINGS
	FGameplayAbilitySpec(const FGameplayAbilitySpec&) = default;
	FGameplayAbilitySpec(FGameplayAbilitySpec&&) = default;
	FGameplayAbilitySpec& operator=(const FGameplayAbilitySpec&) = default;
	FGameplayAbilitySpec& operator=(FGameplayAbilitySpec&&) = default;
	~FGameplayAbilitySpec() = default;
PRAGMA_ENABLE_DEPRECATION_WARNINGS

	FGameplayAbilitySpec()
		: Ability(nullptr), Level(1), InputID(INDEX_NONE), SourceObject(nullptr), ActiveCount(0), InputPressed(false), RemoveAfterActivation(false), PendingRemove(false), bActivateOnce(false)
	{ }

	/** Version that takes an ability class */
	FGameplayAbilitySpec(TSubclassOf<UGameplayAbility> InAbilityClass, int32 InLevel = 1, int32 InInputID = INDEX_NONE, UObject* InSourceObject = nullptr);

	/** Version that takes an ability CDO, this exists for backward compatibility */
	FGameplayAbilitySpec(UGameplayAbility* InAbility, int32 InLevel = 1, int32 InInputID = INDEX_NONE, UObject* InSourceObject = nullptr);

	/** Version that takes an existing spec def */
	FGameplayAbilitySpec(FGameplayAbilitySpecDef& InDef, int32 InGameplayEffectLevel, FActiveGameplayEffectHandle InGameplayEffectHandle = FActiveGameplayEffectHandle());

	/** Handle for outside sources to refer to this spec by */
	UPROPERTY()
	FGameplayAbilitySpecHandle Handle;
	
	/** Ability of the spec (Always the CDO. This should be const but too many things modify it currently) */
	UPROPERTY()
	TObjectPtr<UGameplayAbility> Ability;
	
	/** Level of Ability */
	UPROPERTY()
	int32	Level;

	/** InputID, if bound */
	UPROPERTY()
	int32	InputID;

	/** Object this ability was created from, can be an actor or static object. Useful to bind an ability to a gameplay object */
	UPROPERTY()
	TWeakObjectPtr<UObject> SourceObject;

	/** A count of the number of times this ability has been activated minus the number of times it has been ended. For instanced abilities this will be the number of currently active instances. Can't replicate until prediction accurately handles this.*/
	UPROPERTY(NotReplicated)
	uint8 ActiveCount;

	/** Is input currently pressed. Set to false when input is released */
	UPROPERTY(NotReplicated)
	uint8 InputPressed:1;

	/** If true, this ability should be removed as soon as it finishes executing */
	UPROPERTY(NotReplicated)
	uint8 RemoveAfterActivation:1;

	/** Pending removal due to scope lock */
	UPROPERTY(NotReplicated)
	uint8 PendingRemove:1;

	/** This ability should be activated once when it is granted. */
	UPROPERTY(NotReplicated)
	uint8 bActivateOnce : 1;

	/** Cached GameplayEventData if this ability was pending for add and activate due to scope lock */
	TSharedPtr<FGameplayEventData> GameplayEventData = nullptr;

	/** Activation state of this ability. This is not replicated since it needs to be overwritten locally on clients during prediction. */
	UPROPERTY(NotReplicated)
	FGameplayAbilityActivationInfo	ActivationInfo;

	/** Optional ability tags that are replicated.  These tags are also captured as source tags by applied gameplay effects. */
	UPROPERTY()
	FGameplayTagContainer DynamicAbilityTags;

	/** Non replicating instances of this ability. */
	UPROPERTY(NotReplicated)
	TArray<TObjectPtr<UGameplayAbility>> NonReplicatedInstances;

	/** Replicated instances of this ability.. */
	UPROPERTY()
	TArray<TObjectPtr<UGameplayAbility>> ReplicatedInstances;

	/**
	 * Handle to GE that granted us (usually invalid). FActiveGameplayEffectHandles are not synced across the network and this is valid only on Authority.
	 * If you need FGameplayAbilitySpec -> FActiveGameplayEffectHandle, then use AbilitySystemComponent::FindActiveGameplayEffectHandle.
	 */
	UPROPERTY(NotReplicated)
	FActiveGameplayEffectHandle	GameplayEffectHandle;

	/** Passed on SetByCaller magnitudes if this ability was granted by a GE */
	TMap<FGameplayTag, float> SetByCallerTagMagnitudes;

	/** Returns the primary instance, used for instance once abilities */
	UGameplayAbility* GetPrimaryInstance() const;

	/** interface function to see if the ability should replicated the ability spec or not */
	bool ShouldReplicateAbilitySpec() const;

	/** Returns all instances, which can include instance per execution abilities */
	TArray<UGameplayAbility*> GetAbilityInstances() const
	{
		TArray<UGameplayAbility*> Abilities;
		Abilities.Append(ReplicatedInstances);
		Abilities.Append(NonReplicatedInstances);
		return Abilities;
	}

	/** Returns true if this ability is active in any way */
	bool IsActive() const;

	void PreReplicatedRemove(const struct FGameplayAbilitySpecContainer& InArraySerializer);
	void PostReplicatedAdd(const struct FGameplayAbilitySpecContainer& InArraySerializer);

	FString GetDebugString();
};
  • GameplayAbility가 활성화 가능한 Ability가 정의된 후에 ASC에 존재하게 된다.
    • GameAbility Class
    • GameAbility Level
    • Input Binding
    • GameplayAbility Class로부터 분리되어야만 하는 runtime state
  • GameAbility가 Server에서 부여되면,
    Server는 해당 Ability의 GameplayAbilitySpec을 활성화 되어야 할 대상 Client에 Replicate 한다.
  • GameplayAbilitySpec을 활성화 하면 Instance Policy에 따라 GameplayAbility Instance가 생성된다.

Passing Data to Abilities

  • GameplayAbility의 범용적인 패러다임은 Activate -> General Data -> Aply -> End이다.
    • 하지만 때로는 이미 존재하는 Data를 기반으로 동작해야 하기도 하다.
  • GAS는 몇 가지 Gameplay Ability 외부의 데이터를 사용하는 옵션을 제공한다.

Activate GameplayAbility by Event

더보기
/**
 * This function can be used to trigger an ability on the actor in question with useful payload data.
 * NOTE: GetAbilitySystemComponent is called on the actor to find a good component, and if the component isn't
 * found, the event will not be sent.
 */
UFUNCTION(BlueprintCallable, Category = Ability, Meta = (Tooltip = "This function can be used to trigger an ability on the actor in question with useful payload data."))
static void SendGameplayEventToActor(AActor* Actor, FGameplayTag EventTag, FGameplayEventData Payload);
  • Payload가 있는 Data를 포함한 Event로 Gameplay Ability를 활성화
  • Event의 Payload는 Client에서 Server로 local predict 된 Gameplay Ability를 Replicate한다.
  • 다만 Ability를 Input Bind로 활성화 하는 것을 방지한다는 단점이 있다.

AbilityTask_WaitGameplayEvent 

  • Gameplay Ability가 활성화 된 후 Payload Data를 Event로 받도록 설정
  • Event Payload를 보내는 과정은 Gameplay Ability가 Event로 활성화 되는 것과 같다.
  • Event가 Ability Task로 REplicate 되지 않고, 오직 LocalOnly나 ServerOnly만 가능하다는 단점이 있다.

Use TargetData

  • Custom Target Data를 선언하여 Client와 Server 사이에 임시 Data 전달

Store Data on the OwnerActor or AvatarActor

  • OwnerActor나 AvatarActor, 혹은 Reference를 갖을 수 있는 Object의 Replicated Varaiable에 저장
  • 이 방법은 Input Bind로 활성화되는 Gameplay Ability로 작업하기에 가장 유연한 방법이다.
  • 하지만 Data를 사용하는 때에 Replication으로 동기화가 되었음을 보장할 수 없다.
    • Replicated Variable을 설정 한 직후 Gameplay Ability를 활성화 하는 경우에
      Packet loss로 인한 동기화 오류가 발생할 여지가 있다.

Ability Cost and Cooldown

  • Gameplay Ability는 선택적으로 Cost나 Cooldown 기능을 제공한다.
  • Cost
    • Instant GE로 실행할 때 반드시 충족해야 하는 ASC에서 사전에 선언된 Gameplay Ability가 Attribute의 요구량
  • Cooldown
    • Duration GE 가 만료된 이후 재실행 될 때까지 Gameplay Ability의 재실행을 방지하는 Duration
  • CanActivateAbility() 함수에서 해당 Gameplay Ability를 소유한 ASC에서 Cost와 Cooldown을 체크한다.
더보기
/** Checks cost. returns true if we can pay for the ability. False if not */
virtual bool CheckCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const;

/** Checks cooldown. returns true if we can be used again. False if not */
virtual bool CheckCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr) const;
  • Activate()가 호출된 후, 선택적으로 Cost와 Cooldown 관련 함수가 호출된다.
더보기
virtual bool CommitAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr);
virtual bool CommitAbilityCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const bool ForceCooldown, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr);
virtual bool CommitAbilityCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, OUT FGameplayTagContainer* OptionalRelevantTags = nullptr);

/** Applies CooldownGameplayEffect to the target */
virtual void ApplyCooldown(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const;
/** Applies the ability's cost to the target */
virtual void ApplyCost(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo) const;
  • Designer는 Cost와 Cooldown이 동시에 적용되지 않아야 하면 이를 분리할 수 있다.
  • 마지막으로 GameplayAbility는 Cost나 Cooldown으로 실행이 실패 했을 때 관련해서 한번 더 체크한다.
  • ASC의 Attribute는 GameplayAbility가 활성화 되면서 값이 변경되어 Cost 조건을 만족하지 못할 수도 있다.

Leveling Up Abilities

기존 Ability 부여를 해제하고 재부여

  • ASC에서 GameplayAbility를 제거
  • 다음 Level의 GameplayAbility를 Server에서 부여
  • 이 제거 동작은 활성화 될 때 발생한다.

GameplayAbilitySpec의 Level을 증가

  • Server에서 Level이 오르는 GameplayAbilitySpec을 탐색
  • 해당 GameplayAbilitySpec을 dirty 처리해서 Client에 Replicate 되도록 처리
  • GameplayAbility가 활성화 될 때 제거되지 않음

비교

  • Level이 상승할 때 해당 Gameplay Ability를 취소할 지 말지 정도의 차이가 있다.
  • 두가지 방법 모두 Gameplay Ability에 따라 매우 빈번하게 사용할 것이다.

Gameplay Ability Set

  • Input Bind와 초기 Gameplay Ability 정보를 담고 있는 편의성 Data Asset
    • Gameplay Ability를 부여하는 로직이 있는 캐릭터에게 유용
    • Subclass에서 외부 로직이나 Property를 포함시킬 수 있다.
    • Paragon에서는 영웅들이 소유하고 있는 고유한 Gameplay Ability를 들고 있는 Gameplay Ability Set을
      각각 가지고 있다.

Ability Batching

  • 전통적인 Gameplay Ability는 LifeCycle동안 Client에서 Server로 최소 2, 3개의 RPC를 보낸다.
    • CallServerTryActivateAbility()
    • ServerSetReplicatedTargetData()
      • 선택적
    • ServerEndAbility
  • 만약 위 모든 동작을 Frame에서 하나의 Atomic Group으로 수행할 수 있다면,
    모든 RPC를 하나의 RPC로 최적화 할 수 있다.
  • GAS는 이런 경우에 대해 Ability Batching 기능을 통해 RPC 최적화를 제공한다.
    • 대표적인 예시는 Hitscan 총기류이다.
    • Hitscan 총기류 활성화, Line Trace, Target Data를 서버로 전달을 한번에 처리한다.
  • 다만 Ability Batching은 ASC에서 Default로는 Disable 처리되어 있다.
    • 이를 사용하려면 아래 함수를 Override 해야 한다.
더보기
virtual bool ShouldDoServerAbilityRPCBatch() const { return false; }
  • Ability Batching을 활성화 하였다면, Batch 하고 싶은 Ability를 활성화 하기 전에
    FScopedServerAbilityRPCBatcher 구조체를 미리 만들어야 한다.
더보기
/** Helper struct for defining ServerRPC batch windows. If null ASC is passed in, this becomes a noop. */
struct GAMEPLAYABILITIES_API FScopedServerAbilityRPCBatcher
{
	FScopedServerAbilityRPCBatcher(UAbilitySystemComponent* InASC, FGameplayAbilitySpecHandle InAbilityHandle);
	~FScopedServerAbilityRPCBatcher();

private:

	UAbilitySystemComponent* ASC;
	FGameplayAbilitySpecHandle AbilityHandle;
	FScopedPredictionWindow ScopedPredictionWindow;
};
  • 이 구조체는 Scope 안의 Ability들을 모두 Batch한다.
    • 한 번 FScopedServerAbilityRPCBatcher의 Scope를 벗어나면, 활성화 된 어떠한 Ability들도 Batch 되지 않는다.
  • FScopedServerAbilityRPCBatcher는 RPC 호출을 가로채는 대신 구조체에 Batch 할 수 있는 message를 Batch 한다.
  • FScopedServerAbilityRPCBatcher의 Scope를 벗어날 때,
    UASC::EndServerAbilityRPCBatch() 함수를 통해 Batch 구조체를 Server로 RPC한다.
    • Server는 UASC::ServerAbilityRPCBatch_Internal 함수를 통해 Batch RPC를 받는다.
더보기
virtual void EndServerAbilityRPCBatch(FGameplayAbilitySpecHandle AbilityHandle);

// Overridable function for sub classes
virtual void ServerAbilityRPCBatch_Internal(FServerAbilityRPCBatch& BatchInfo);
  • 이 때 BatchInfo는 다음 Flag 정보를 포함한다.
    • Ability가 반드시 끝나야 하는지 여부
    • Activity가 활성화 되었을 때 Press Input이 발생했는지 여부
    • TargetData가 포함되었는지 여부
  • ServerAbilityRPCBatch_Internal은 Batch가 정상 동작하는지 확인하기 위해 Break Point를 걸기 매우 적절한 함수다.
  • 혹은 이를 대신 해, 아래 커맨드를 입력해 로그를 남길 수 있다.
더보기
AbilitySystem.ServerRPCBatching.Log 1
  • 다음은 Ability Batch에 대한 간단한 예시코드이다.
더보기
bool UGSAbilitySystemComponent::BatchRPCTryActivateAbility(FGameplayAbilitySpecHandle InAbilityHandle, bool EndAbilityImmediately)
{
	bool AbilityActivated = false;
	if (InAbilityHandle.IsValid())
	{
		FScopedServerAbilityRPCBatcher GSAbilityRPCBatcher(this, InAbilityHandle);
		AbilityActivated = TryActivateAbility(InAbilityHandle, true);

		if (EndAbilityImmediately)
		{
			FGameplayAbilitySpec* AbilitySpec = FindAbilitySpecFromHandle(InAbilityHandle);
			if (AbilitySpec)
			{
				UGSGameplayAbility* GSAbility = Cast<UGSGameplayAbility>(AbilitySpec->GetPrimaryInstance());
				GSAbility->ExternalEndAbility();
			}
		}

		return AbilityActivated;
	}

	return AbilityActivated;
}

Replication

  • 내부 상태 및 GE Replication을 지원
    • 이를 비활성화 하면 네트워크 대역폭과 CPU 사이클 절약이 가능

Gameplay Ability Replication Policy

  • Ability가 Network를 통해 자체 Instance를 Replicate 할지, 상태를 업데이트 할지, GE를 전송할지 제어하는 수단
  • 이름이 기능을 명확히 알려주지 않으며 사용하지 않는 것을 권장하는 옵션
    • GameplayAbilitySpec이 기본적으로 Server에서 Client로 Replicate된다.
    • 또한 GameplayAbility는 AbilityTask나 GameplayCue를 통해 Replicate 되거나 RPC를 호출한다.
    • 근시일에 이 기능을 제거할 것이라고 Epic의 Dave Ratti가 업급하기도 하였다.

Gameplay Net Execution Policy

  • Gameplay Ability가 로컬에서 predict 되는지 여부를 결정
  • GE의 추가 cost, cooldown과 관련된 기본적인 동작을 포함함.

Local Predicted

  • 반응성과 정확성 사이에 균현이 잘 잡힌 옵션
  • Client가 명령을 내리면 Local Client에서 Ability가 즉시 실행된다.
    • 하지만 실제 영향이 어땠는지에 대한 최종 결정은 Server가 내린다.
    • 그리고 이 값을 Client에 Override 할 수 있다.
  • 실제로 Client는 Server에 Ability 실행 권한을 요청하지만, Server가 동의할 것을 예상하고 Local로 진행한다.
  • Client는 Local에서 Ability의 Behaviour를 예측하므로
    서버와의 Behaviour가 다르지 않으면 지연 없이 매끄럽게 느껴진다.

Local Only

  • Client가 단순히 Local에서 Ability를 실행
  • Server에 Replicate하지 않는다.
    • Listen Server Host, Single Player Game에서는 서버에서도 실행되기도 한다.
    • Dedicate Server에서는 적용되지 않는다.
  • 이 Ability로 Client가 영향을 주는 모든 것은 일반 Replicate Protocol을 따른다.
    • 여기에 서버에서 보정을 받을 가능성도 포함된다.

Server Initiated

  • Server에서 시작하는 Abilit가 Client에 전파된다.
    • 종종 Client 관점에서 Server의 실제 상황과 더욱 정확하게 동작하기도 하지만,
      Ability를 사용하는 Client에서는 Delay가 관측된다.
    • Delay가 매우 짧더라도 긴박한 상황에서는 매끄럽지 않은 느낌을 받는다.

Server Only

  • Server에서만 Ability가 실행되고 Client로 Replicate 되지 않는다.
  • 이 Ability가 변경하는 모든 변수는 평소처럼 Replicate 된다.
    • Ability가 Server Authorized Data에 영향을 미칠 수 있고, 그 뒤에 Client에 Replicate 된다는 의미이다.
    • 이 방식을 통해 Ability를 Server에서만 실행 하더라도 Client가 관측하는 Effect를 계속 보유할 수 있다.

Net Security Policy

  • 대상 Ability가 Network 환경에서 어디서 실행되어야만 하는지 정의
  • Client에서 제한된 기능을 동작하는 것을 방지한다.

ClientOrServer

  • 아무런 보안이 필요 없음.
  • Server, Client 모두 자유롭게 실행과 제거를 할 수 있다.

ServerOnlyExecution

  • Client에서 이 Ability에 대한 실행 요청을 Server가 거부한다.
  • Client는 여전히 Server에 Ability의 취소나 종료 요청을 보낼 수는 있다.

ServerOnlyTermination

  • Client에서 이 Ability에 대한 취소/종료 요청을 Server가 거부한다.
  • Client는 여전히 Ability의 실행 요청을 보낼 수는 있다.

ServerOnly

  • 오직 Server에서만 Ability에 대한 실행과 제거를 조절한다.
  • Client에서 보내는 어떠한 요청도 모두 무시된다.

Server Respects Remote Ability Cancellation

  • Client의 GameplayAbility가 취소 혹은 정상적인 동작 종료로 끝난다면,
    서버에서 작업이 완수가 되었는지와 무관하게 강제로 끝낸다는 의미의 옵션
  • 특히 정상적인 종료의 케이스의 경우, Latency가 높은 유저의 경우 매우 중요한 문제를 야기할 수 있다.
  • 때문에 이 옵션도 사용하지 않는 것을 권장한다.

Replicate Input Directly

  • Client에서 발생하는 모든 Input Press/Release를 Server로 Replicate 하는 옵션
    • Epic에서는 이 옵션 사용을 지양하는 것을 권장한다.
    • 대신 ASC에 Input이 Bind 되어 있다면 기존 입력 관련해 Ability Task에서 제공하는
      Generic Replicated Event 사용을 권장한다.
더보기
/** Direct Input state replication. These will be called if bReplicateInputDirectly is true on the ability and is generally not a good thing to use. (Instead, prefer to use Generic Replicated Events). */
UFUNCTION(Server, reliable, WithValidation)
void ServerSetInputPressed(FGameplayAbilitySpecHandle AbilityHandle);

UFUNCTION(Server, reliable, WithValidation)
void ServerSetInputReleased(FGameplayAbilitySpecHandle AbilityHandle);

Instancing Policy

  • Gamplay Ability를 실행하면 보통 해당 Ability Type의 새 Object가 Spawn 되어 상황을 추적한다.
    • BR, MOBA, MMO, RTS 게임처럼 수백의 Player나 AI가 전투를 벌이는 경우,
      Ability 실행 빈도가 매우 높아져 Object 생성이 많아져 성능에 부정적인 영향을 줄 수 있다.
  • 이를 해결하기 위해 Ability에 3가지 Instancing Policy를 제공해 성능과 기능 사이 균형을 맞출 수 있다.

Instanced per Execution

  • Ability를 실행할 때마다 Ability Object의 사본을 Spawn

장점

  • BP Graph와 멤버 변수를 자유롭게 사용 할 수 있음
  • 실행 시작 시 모든 값이 Default로 초기화 된다.
  • 가장 간단하게 구현할 수 있는 Instancing Polity

단점

  • 대규모 Overhad가 수반되어 자주 실행되지 않는 Ability에 이상적
    • ex) MOBA의 궁극기

Instanced per Actor

  • Ability를 처음 실행하면 Actor마다 하나의 Ability Instance를 Spawn
    • 향후 재실행 시 해당 Instance를 재사용
  • Ability를 실행할 때마다 멤버 변수를 지워야 하지만, 여러번 실행에서 정보를 절약할 수 있다.
  • 각 Instance는 Ability에 Replicated Object를 통해 변수 변화와 RPC를 처리 할 수 있다.
    • 하지만 실행할 때마다 새 Object를 Spawn하느라 네트워크 대역폭과 CPU 시간이 낭비되지 않는다.
    • 때문에 Replication에 이상적이다.
  • 규모가 큰 상황에서 Performance가 뛰어난 정책
    • 많은 수의 Actor가 처음 Ability를 사용할 때에만 Object를 Spawn하기 때문

Non-Instanced

  • 전체 Category 중 가장 효율적인 Instancing Policy
  • Ability를 실행할 때마다 Object Spawn 대신 Class Default Object를 사용
  • 하지만 이런 효용성으로 인해 몇 가지 제약사항이 발생한다.
    • C++로만 작성이 가능하다.
    • Instance가 없는 Ability의 BP Class를 생성할 수는 있지만,
      노출된 Property의 Default 값을 변경할 때에만 사용 가능하다.
    • Ability는 실행 중에 멤버 변수를 변경하거나 Delegate Bind를 하면 안된다.
    • 변수를 Replicate 하거나 RPC를 처리할 수 없다.
  • 내부적인 변수 저장과 Data Replication이 필요 없는 Ability에만 사용
    • 다만 Ability의 사용자가 Attribute를 설정하는 것은 가능
  • ex) RTS나 MOBA 게임에서 유닛이 사용하는 기본 공격

Trigger with Gameplay Event

  • 일반 Channel을 통하지않고도 어떤 Context의 Data Payload를 전송해
    Ability를 직접 Trigger 하도록 전달될 수 있는 Struct
  • 일반적으로 다음 기능을 제공
    • Actor에 SendGameplayEventToActor을 호출
    • IAbilitySystmInterface와 Gameplay Event에 필요한 Context 정보를 구현하는 Actor 제공
  • 하지만 ASC에서 HandleGameplayEvent를 바로 호출해도 문제 없다.
  • Gameplay Ability를 호출하는 정상적인 경로가 아니기에
    필요한 Context 정보는 FGameplayEventData 구조체로 전달한다.
  • 이 구조체는 범용이기 때문에 특정 Gameplay Event나 Ability용으로 확장되지 않는다.
    • 하지만 ContextHandle 관련 필드에 부가 정보를 제공하면 Ability나 Gameplay Event용으로 사용할 수 있다.
  • Gameplay Event가 Gameplay Ability를 Trigger하면, Activate Ability 대신 Activate Ability From Event를 사용한다.
    • Activate Ability From Event는 부가 Context Data를 parameter로 제공할 수 있다.
  • Ability가 Gameplay Event에 반응하려면 반드시 Activate Ability from Event로 처리되어야 한다.
    • 하지만 BP에서 구현되고 나면 Activate Ability를 대신해 모든 Acitivate Traffic을 받게 된다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Prediction  (0) 2024.05.21
[GAS] Gameplay Cue  (0) 2024.05.21
[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-ability-tasks-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 태스크 | 언리얼 엔진 5.4 문서 | Epic Developer Community

어빌리티 태스크 클래스의 개요입니다.

dev.epicgames.com

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-at

 

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. - tranek/GASDocumentation

github.com

  • 일반적인 Gameplay Task Class의 특수한 형태로 GA와 함께 작동하도록 되어있다.
  • GAS를 사용하는 게임에는 보통 고유한 Gameplay 기능을 구현하는 다양한 Custom Ability Task가 포함되어 있다.
  • 이러한 Task는 GA 실행 중 비동기로 수행하고, Native C++에서 Delegate를 호출하거나
    BP에서 하나 이상의 출력 실행 핀을 이동하여 실행 흐름에 영향을 미칠 수 있다.
    • 이를 통해 Ability가 여러 Frame에서 실행되기도 하고, 동일 Frame에 여러 개의 다른 기능을 수행할 수 있다.
  • 대부분의 Ability Task에는 즉시 발동하는 실행 핀이 있다.
    • 때문에 TAsk 시작 후에도 BP가 계속 실행 된다.
  • 이 외에 Delay 후에 발동되거나, 발생하지 않을 수도 있는 조건부 이벤트에 뒤따라 발동되는 Task별 핀이 있기도 하다.
  • Ability Task를 종료시킬 수 있는 방법은 2가지이다.
    • EndTask 함수를 호출하여 자체적으로 종료
    • 해당 Task를 실행한 Gameplay Ability가 끝날 때 자동 종료하도록 대기
  • 이는 Phantom Ability Task가 실행되어 CPU 및 메모리 누수가 나는 것을 방지합니다.
  • Ability Task는 Network 여부와 상관 없이 모두 사용하도록 설계되었지만 ,직접 자체적으로 업데이트 하지 않는다.
    • 일반적으로 Ability Task는 Replicate되는 GA에서 생성된다.
    • 하지만 실행 흐름을 결정하기 위해 Input이나 Network Variable 등 Replicate 된 정보를 사용한다.
    • 때문에 간접적으로 동기화 상태를 유지한다.

Definition

  • GA는 오직 한 Frame에서 동작하고 이 이상의 유연성을 제공하지 않는다.
  • 여러 Frame에서 동작 하거나 시간이 지난 후 특정 시점에 Delegate를 받으려면 Gmaplay Task를 써야 한다.
  • AbilityTask는 일반적인 Gameplay Task의 특수한 형태로 다음 기능들을 제공한다.
    • RootMotionSource로 움직이는 Character에 대한 Task
    • Animation Montage를 재생시키는 Task
    • Attribute 변화에 대한 반응 Task
    • GE 변화에 대한 반응 Task
    • Player Input에 대한 반응 Task
  • UAbilityTask 생성자는 동시에 실행될 수 있는 AbilityTask를 게임 전체에서 최대 1000개로 제한한다.
    • 이는 캐릭터가 수 백개가 나오는 RTS 게임 설계에서 유념해둬야 한다.

Custom Ability Task

  • Custom Ability Task 생성은 매우 빈번할 것이다.
  • AbilityTask는 다음으로 구성되어 있다.
    • 새 AbilityTask Instance를 생성하는 static function
    • AbilityTask가 동작을 완수 했을 때 발생하는 delegate
    • Activate() 함수
      • 주요 작업을 시행하고 외부 delegate에 bind
    • OnDestroy()
      • 외부 delegate를 포함한 모든 clean up
    • 외부 Delegate에 Bind 되는 Callback 함수
    • 멤버 변수와 다른 내부 함수
  • AbilityTask는 오직 하나의 Output Delegate를 선언할 수 있다.
    • 모든 Output Delegate 는 파라미터가 있든 없든 이 타입이어야 한다.
    • 파라미터를 사용 안 하면 Default Value가 전달된다.
  • Ability Task는 오직 이를 소지한 GameplayAbility의 서버/클라이언트에서만 동작한다.
    • 하지만 AbilityTask::InitSimulatedTask 함수를 override하여 그 안에서 bSimulatedTask 플래그와
      다른 Replicate 되는 멤버변수를 설정하여 Simulated Client에서 동작하도록 할 수 있다.
    • 이는 모든 Movement AbilityTask를 replicate 하고 싶지 않지만 simulate는 하고 싶을 때 유용하다.
  • 모든 RootMotionSource AbilityTask는 이것을 사용한다.
  • 또한 AbilityTask::TickTask를 override하여 bTickTask 플래그를 활성화 하면 Tick을 사용할 수 있다.
    • 이는 프레임에 따라 값이 부드럽게 바뀌는 경우에 유용하다.

Using Ability Task

C++

더보기
UGDAT_PlayMontageAndWaitForEvent* Task = UGDAT_PlayMontageAndWaitForEvent::PlayMontageAndWaitForEvent(this, NAME_None, MontageToPlay, FGameplayTagContainer(), 1.0f, NAME_None, false, 1.0f);
Task->OnBlendOut.AddDynamic(this, &UGDGA_FireGun::OnCompleted);
Task->OnCompleted.AddDynamic(this, &UGDGA_FireGun::OnCompleted);
Task->OnInterrupted.AddDynamic(this, &UGDGA_FireGun::OnCancelled);
Task->OnCancelled.AddDynamic(this, &UGDGA_FireGun::OnCancelled);
Task->EventReceived.AddDynamic(this, &UGDGA_FireGun::EventReceived);
Task->ReadyForActivation();

BP

  • BP에서는 ReadyForActivation()을 호출할 필요가 없다.
    • K2Node_LatentGameplayTaskCall에서 자동으로 호출해준다.
    • 해당 AbilityTask에 있는 경우 BeginSpawningActor(), FinishSpawningActor()도 자동으로 호출해준다.

Root Motion Source Ability Task

  • GAS에서 캐릭터를 이동시키는 AbilityTask는 CharacterMovementComponent에
    후킹 된 Root Motion Source를 이용한다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Gameplay Cue  (0) 2024.05.21
[GAS] Gameplay Ability  (0) 2024.05.17
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-effects-for-the-gameplay-ability-system-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템용 게임플레이 어트리뷰트 및 게임플레이 이펙트 |

게임플레이 어빌리티 시스템 내 어트리뷰트 및 이펙트의 개요입니다.

dev.epicgames.com

 

 

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-ge

 

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. - tranek/GASDocumentation

github.com

 

  • GAS에서 Attribute를 변경하는 방식
  • UGameEffect를 상속받아 구현
  • BlueprintNative로 작업하지 않는 대신, 변수를 통해 환경설정을 한다.
  • GameplayEffects(이하 GE)는 Instance를 생성하지 않는다.
    • Ability나 ASC가 GE를 등록하기 원하면, GE는 ClassDefaultObjects로부터 GameplayEffectSpec를 생성한다.
    • 성공적으로 생성된 GameplayEffectSpec은 FActiveGameplayEffect에 추가되어
      ASC의 ActiveGameplayEffects에 저장된다.

Apply

  • GE는 GameplayAbilities와 ASC에서 제공하는 수 많은 ApplyGameplayEffectTo가 포함된 함수로 등록한다.
  • 또한 Duration, Infinite GE가 ASC에 등록될 때 Delegate를 받을 수 있다.
    • 더보기
      /** Delegate for when an effect is applied */
      DECLARE_MULTICAST_DELEGATE_ThreeParams(FOnGameplayEffectAppliedDelegate, UAbilitySystemComponent*, const FGameplayEffectSpec&, FActiveGameplayEffectHandle);
      
      /** Called on server whenever a GE is applied to self. This includes instant and duration based GEs. */
      FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToSelf;
      
      /** Called on server whenever a GE is applied to someone else. This includes instant and duration based GEs. */
      FOnGameplayEffectAppliedDelegate OnGameplayEffectAppliedDelegateToTarget;
      
      /** Called on both client and server whenever a duration based GE is added (E.g., instant GEs do not trigger this). */
      FOnGameplayEffectAppliedDelegate OnActiveGameplayEffectAddedDelegateToSelf;
      
      /** Called on server whenever a periodic GE executes on self */
      FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnSelf;
      
      /** Called on server whenever a periodic GE executes on target */
      FOnGameplayEffectAppliedDelegate OnPeriodicGameplayEffectExecuteDelegateOnTarget;
      
      
      /** This ASC has successfully applied a GE to something (potentially itself) */
      void OnGameplayEffectAppliedToTarget(UAbilitySystemComponent* Target, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle);
      void OnGameplayEffectAppliedToSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecApplied, FActiveGameplayEffectHandle ActiveHandle);
      void OnPeriodicGameplayEffectExecuteOnTarget(UAbilitySystemComponent* Target, const FGameplayEffectSpec& SpecExecuted, FActiveGameplayEffectHandle ActiveHandle);
      void OnPeriodicGameplayEffectExecuteOnSelf(UAbilitySystemComponent* Source, const FGameplayEffectSpec& SpecExecuted, FActiveGameplayEffectHandle ActiveHandle);
  • 위 함수들은 LocalRole에 따라 호출 여부가 다르다.
    • Authority: 언제나 항상 호출
    • Autonomous: 대상 GE의 Replication Mode가 Full이거나 Mixed인 경우에만 호출 됨
    • Simulated: 대상 GE의 Replication Mode가 Full일 때에만 호출 됨

 

Remove

  • GE는 GameplayAbilities와 ASC에서 RemoveActiveGameplayEffect가 포함된 함수들에 의해 제거된다.
  • 또한 Duration, Infinite Ge는 제거될 때 Delegate를 받을 수 있다.
    • 더보기
      /** Contains all of the gameplay effects that are currently active on this component */
      UPROPERTY(Replicated)
      FActiveGameplayEffectsContainer ActiveGameplayEffects;
          
          
      DECLARE_MULTICAST_DELEGATE_OneParam(FOnGivenActiveGameplayEffectRemoved, const FActiveGameplayEffect&);
      DECLARE_MULTICAST_DELEGATE_OneParam(FOnActiveGameplayEffectRemoved_Info, const FGameplayEffectRemovalInfo&);
      
      
      /** Called when any gameplay effects are removed */
      FOnGivenActiveGameplayEffectRemoved& OnAnyGameplayEffectRemovedDelegate();
      /** Returns delegate structure that allows binding to several gameplay effect changes */
      FOnActiveGameplayEffectRemoved_Info* OnGameplayEffectRemoved_InfoDelegate(FActiveGameplayEffectHandle Handle);
  • 위 함수들은 LocalRole에 따라 호출 여부가 다르다.
    • Authority: 언제나 항상 호출
    • Autonomous: 대상 GE의 Replication Mode가 Full이거나 Mixed인 경우에만 호출 됨
    • Simulated: 대상 GE의 Replication Mode가 Full일 때에만 호출 됨

 

Duration

더보기
/** Gameplay effect duration policies */
UENUM()
enum class EGameplayEffectDurationType : uint8
{
	/** This effect applies instantly */
	Instant,
	/** This effect lasts forever */
	Infinite,
	/** The duration of this effect will be specified by a magnitude */
	HasDuration
};



Instant

  • Attribute의 BaseValue를 즉시 영구적으로 바꾼다.
  • GameplayTags 사용이 불가하다.
  • GameplayCue, GameplayTags에서 Execute를 호출

Infinite

  • Attribute의 CurrentValue를 일시적으로 바꾼다.
  • GE가 삭제될 때 GameplayTags도 삭제된다.
  • ASC에서 스스로 만료되지 않기 때문에 반드시 명시적으로 삭제되어야 한다.
  • GameplayCue에서 Add, Remove를 호출. 
    • GE가 발생하는 시점과 효과가 발동하는 시점을 다르게 등록할 수 있음
    • Audio, Visual Effect 타이밍 구성에도 유용함

HasDuration

  • Attribute의 CurrentValue를 일시적으로 바꾼다.
  • GE가 만료되거나 삭제될 때 GameplayTags도 삭제된다.
  • Duration은 UGameplayEffect BP로 지정된다.
  • GameplayCue에서 Add, Remove를 호출.
    • GE가 발생하는 시점과 효과가 발동하는 시점을 다르게 등록할 수 있음
    • Audio, Visual Effect 타이밍 구성에도 유용함

Period

더보기
/** Period in seconds. 0.0 for non-periodic effects */
UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Duration|Period", meta=(EditCondition="DurationPolicy != EGameplayEffectDurationType::Instant", EditConditionHides))
FScalableFloat	Period;
  • Duration 타입이 HasDuration, Infinite인 경우에만 적용
  • GE의 Modifiers나 Executions를 등록된 수치의 seconds만큼 대기 했다가 실행
  • 이 때 Delay를 갖고 발생하는 Effects들은 다음 조건을 만족하는 경우 Instant GE로 취급한다.
    • Attribute의 BaseValue를 변경하는 경우
    • GameplayCue를 실행하는 경우
  • 도트딜을 작업할 때 유용함

Modifiers

더보기
/**
 * FGameplayModifierInfo
 *	Tells us "Who/What we" modify
 *	Does not tell us how exactly
 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayModifierInfo
{
	GENERATED_USTRUCT_BODY()
	
	/** The Attribute we modify or the GE we modify modifies. */
	UPROPERTY(EditDefaultsOnly, Category=GameplayModifier, meta=(FilterMetaTag="HideFromModifiers"))
	FGameplayAttribute Attribute;

	/** The numeric operation of this modifier: Override, Add, Multiply, etc  */
	UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
	TEnumAsByte<EGameplayModOp::Type> ModifierOp = EGameplayModOp::Additive;

	/** Magnitude of the modifier */
	UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
	FGameplayEffectModifierMagnitude ModifierMagnitude;

	/** Evaluation channel settings of the modifier */
	UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
	FGameplayModEvaluationChannelSettings EvaluationChannelSettings;

	UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
	FGameplayTagRequirements	SourceTags;

	UPROPERTY(EditDefaultsOnly, Category=GameplayModifier)
	FGameplayTagRequirements	TargetTags;

	/** Equality/Inequality operators */
	bool operator==(const FGameplayModifierInfo& Other) const;
	bool operator!=(const FGameplayModifierInfo& Other) const;
};


	/** Array of modifiers that will affect the target of this effect */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category=GameplayEffect, meta=(TitleProperty=Attribute))
	TArray<FGameplayModifierInfo> Modifiers;
  • GameplayEffect가 Attribute와 상호작용 하는 방식을 결정
    • Attribute 자체에 대한 수학적 Interaction뿐 아니라, GameplayTag 정보도 포함된다.
  • 각 Modifier는 특정 연산으로 하나의 Attribute만 수정할 책임이 있다.
    • Add
      • Modifier의 특정 Attribute에 결과 값을 더한다.
      • 음수를 사용하여 뺄셈을 구현한다.
    • Multiply
      • Modifier의 특정 Attribute에 결과 값을 곱한다.
    • Divide
      • Modifier의 특정 Attribute에 결과 값을 나눈다.
    • Override
      • Modifier의 특정 Attribute에 결과 값을 덮어씌운다.
  • Attribute의 CurrentValue는 BaseValue에 가해지는 모든 Modifier의 종합 결과이다.
  • Modifier가 어떻게 집계될지에 대한 공식은 다음 함수에 표현되어 있다.
    • 더보기
      float FAggregatorModChannel::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const
      {
      	for (const FAggregatorMod& Mod : Mods[EGameplayModOp::Override])
      	{
      		if (Mod.Qualifies())
      		{
      			return Mod.EvaluatedMagnitude;
      		}
      	}
      
      	float Additive = SumMods(Mods[EGameplayModOp::Additive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Additive), Parameters);
      	float Multiplicitive = SumMods(Mods[EGameplayModOp::Multiplicitive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Multiplicitive), Parameters);
      	float Division = SumMods(Mods[EGameplayModOp::Division], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Division), Parameters);
      
      	if (FMath::IsNearlyZero(Division))
      	{
      		ABILITY_LOG(Warning, TEXT("Division summation was 0.0f in FAggregatorModChannel."));
      		Division = 1.f;
      	}
      
      	return ((InlineBaseValue + Additive) * Multiplicitive) / Division;
      }
    • 위 연산을 마치면 가장 마지막에 Override Modifier가 동작한다.

Modifier Type

Scalable Float

  • 변수를 Row로 갖고 Level을 Column으로 갖는 Data Table을 가르킬 수 있는 구조체
  • Ability의 현재 Level이나 GameplayEffectSpec에서 override 된 다른 Level을 가진 Row의 Value를 자동으로 읽는다.
    • 이 값은 계수에 의해 변동될 수 있다.
  • 만약 특정 DataTable이나 Row가 없다면 값이 1이 되어 계수 값을 가진 하나의 변수로 사용 가능하다.

Attribute Based

  • Source(GameplayEffectSpec을 만든 주체)나 Target(GameplayEffecSpec을 받을 주체)의 Attribute의 Current/Base Value를 기반으로 사용
  • 위 값을 기반으로 계수나 계수 전후의 추가값을 사용하여 수정
  • Snapshooting이 세팅되면 GameplayEffectSpec이 생성될 때 Attribute이 캡쳐 된다.
    • Snapshooting이 설정되지 않으면 GameplayEffeectSpec이 등록 될 때 Attribute가 캡쳐된다.

Custom Calculation Class

  • 복잡한 Modifier에 대해 가장 유연함을 자랑한다.
  • ModifierMagnitudeCalculation class를 사용하고 결과에 계수, 계수 전후 추가값으로 추가 보정이 가능하다.

Set By Caller

  • Runtime 상에서 Ability나 GameplayEffectSpec에서 GameplayEffectSpec 만들면서 GE 외에서 값에 영향을 미치는 경우에 사용
    • 예를 들어 버튼을 Hold한 만큼 데미지가 증가하는 경우 이 타입을 사용한다.
  • 이 타입은 사실 GameplayEffectSpec에 있는 TMap<FGameplayTag, float>
    • Modifier는 그저 제공된 GameplayTag와 관련된 SetByCaller 값을 읽고 연산할 뿐이다.
    • 때문에 Modifier에서 사용하는 SetByCallers는 GameplayTag 버전만 사용 가능하고, FName 버전은 불가하다.
  • Modifier가 SetByCaller 타입으로 선언 되었으나 GameplayEffectSpec에 적절한 GameTag가 없는 경우,
    게임은 Runtime Error를 생성하며 0을 반환한다.
    • 이 케이스는 주로 Divide 연산에서 자주 발생한다.

Multiply/Divide Modifiers

  • 기본적으로 Multiply/Divide Modifier는 Attribute의 BaseValue에 연산이 적용되기 전에 함께 추가된다.
    • 더보기
      /**
       * Helper function to sum all of the mods in the specified array, using the specified modifier bias and evaluation parameters
       * 
       * @param InMods		Mods to sum
       * @param Bias			Bias to apply to modifier magnitudes
       * @param Parameters	Evaluation parameters
       * 
       * @return Summed value of mods
       */
      float FAggregatorModChannel::SumMods(const TArray<FAggregatorMod>& InMods, float Bias, const FAggregatorEvaluateParameters& Parameters)
      {
      	float Sum = Bias;
      
      	for (const FAggregatorMod& Mod : InMods)
      	{
      		if (Mod.Qualifies())
      		{
      			Sum += (Mod.EvaluatedMagnitude - Bias);
      		}
      	}
      
      	return Sum;
      }
      
      /**
       * Evaluates the channel's mods with the specified base value and evaluation parameters
       * 
       * @param InlineBaseValue	Base value to use for the evaluation
       * @param Parameters		Additional evaluation parameters to use
       * 
       * @return Evaluated value based upon the channel's mods
       */
      float FAggregatorModChannel::EvaluateWithBase(float InlineBaseValue, const FAggregatorEvaluateParameters& Parameters) const
      {
      	for (const FAggregatorMod& Mod : Mods[EGameplayModOp::Override])
      	{
      		if (Mod.Qualifies())
      		{
      			return Mod.EvaluatedMagnitude;
      		}
      	}
      
      	float Additive = SumMods(Mods[EGameplayModOp::Additive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Additive), Parameters);
      	float Multiplicitive = SumMods(Mods[EGameplayModOp::Multiplicitive], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Multiplicitive), Parameters);
      	float Division = SumMods(Mods[EGameplayModOp::Division], GameplayEffectUtilities::GetModifierBiasByModifierOp(EGameplayModOp::Division), Parameters);
      
      	if (FMath::IsNearlyZero(Division))
      	{
      		ABILITY_LOG(Warning, TEXT("Division summation was 0.0f in FAggregatorModChannel."));
      		Division = 1.f;
      	}
      
      	return ((InlineBaseValue + Additive) * Multiplicitive) / Division;
      }
  • Multiply/Divide Modifier는 둘 다 1의 Bias Value를 가지고 있다.
    • 1 + (Mod1.Magnitude - 1) + (Mod2.Magnitude - 1) + ...
  • 이러한 식은 예상치 못한 결과를 야기한다.
    • BaseValue에 Multiply/Divide가 수행되기 전에 모든 Modifier를 더해준다.
      • 예를 들어 1.5 Multiply Modifier가 2개가 있을 때 사람들의 예상 값은 1.5 * 1.5 = 2.25이다.
      • 하지만 실제 연산은 1 + 0.5 + 0.5 = 2가 된다.
    • 설계된대로 사용할 수 있는 값에 대한 기록되지 않는 규칙이 존재한다.
      • 첫번째 Modifier 값이 1보다 작고 그 외의 값들이 모두 1 이상 2 미만인 경우
      • 첫번째 Modifier 값이 2보다 큰 경우
    • 이를 해결하기 위해 FAggregatorModeChannel::EvaluateWithBase를 다음과 같이 수정하는 것을 권장한다.
      • 더보기
        float FAggregatorModChannel::MultiplyMods(const TArray<FAggregatorMod>& InMods, const FAggregatorEvaluateParameters& Parameters)
        {
        	float Multiplier = 1.0f;
        
        	for (const FAggregatorMod& Mod : InMods)
        	{
        		if (Mod.Qualifies())
        		{
        			Multiplier *= Mod.EvaluatedMagnitude;
        		}
        	}
        
        	return Multiplier;
        }

Gameplay Tags on Modifiers

  • Modifier에는 SourceTag와 TargetTag를 지정할 수 있다.
    • 이 둘은 GE의 Application Tag Requirements와 동일하게 동작한다.
    • 때문에 Tag는 Effect가 적용될 때에만 고려된다.
  • Attribute Based Modifier는 SourceTagFilter와 TargeTagFilter를 지정할 수 있다.
    • 이는 Modifier의 Source의 규모를 결정할 때,
      Filter에서 제공하는 Tag를 모두 가지고 있는  Modifier를 연산에서 배제한다.

Stacking

  • Default GE는 이전에 생성된 GameplayEffectSpec Instance를 고려하지 않고 새로운 Instance를 등록할 것이다.
    • GE는 새로운 Instance를 추가하는 대신, 기존 Instance의 Stack Count를 바꾸는 방식으로 쌓을 수 있다.
  • 이 기능은 Infinite, Duration GE에서만 동작한다.
  • Stacking은 또한 만료, Duration 갱신, Period 초기화 기능도 제공한다.

Aggregate By Source

  • Target의 Source ASC가 각각 1개의 Instance를 가지는 경우
  • 각 Source가 동일한 수의 Stack Count를 가질 수 있다.

Aggregate By Target

  • Source 무관하게 Target 당 1개의 Instance를 가진다.
  • Souce는 공유된 Stack Count를 관리한다.

Tags

  • GE는 여러 개의 GameplayTagContainers를 가질 수 있다.
    • Designer는 Added/Removed GameplayTagContainer를 수정할 수 있고,
      그 결과는 Compile을 거쳐 Combined GameplayTagContainer에 나타난다.
  • Added Tag는 이 부모 GE에 없는 것고 이 GE에서 추가된 Tag를 지칭한다.
  • Removed Tag는 부모 GE가 가지고 있지만 이 GE가 가지고 있지 않는 Tag를 지칭한다.

Gameplay Effect Asset Tags

  • GE가 가지고 있는 Tag
  • 아무런 기능적 목적이 없고, GE를 표현하기 위해서만 사용된다.

Granted Tags

  • GE에 있지만 GE가 ASC에 등록될 때 전달되기도 하는 Tag
    • GE가 제거될 때 ASC에서도 제거된다.
  • Duration, Infinite GE에서만 동작한다.

Ongoing Tag Requirements

  • 한번만 등록되며 GE가 활성화 되었는지 여부를 알 수 있다.
    • GE가 등록되어 있더라도 이 Tag를 비활성화 할 수 있다.
  • 만약 GE가 Ongoing Tag Requirements가 실패해 비활성화 되었지만 Requirements가 그 뒤에 도착하면,
    GE가 Tag를 다시 활성화 하고 Modifier를 재등록한다.
  • Duration, Infinite GE에서만 동작한다.

Application Tag Requirements

  • GE가 Target에 적용될 수 있는지를 확인하는 Tag
  • 이 Requirements가 맞지 않으면 GE가 적용되지 않는다.

Remove Gameplay Effects with Tags

  • Asset Tag나 Granted Tag에서 이 Tag에 등록된 Tag는 GE가 적용될 때 Target으로부터 제거된다.

Immunity

  • 다른 GE의 동작을 효과적으로 막기 위해 GE는 GameplayTag를 이용해 Immunity를 부여할 수 있다.
  • Application Tag Requirements와 같이 다른 수단을 통햐 Immunity를 효과적으로 부여할 수 있기도 하지만,
    이 System을 사용하면 Immunity로 GE가 차단될 때 delegate를 받을 수 있습니다.
    • 더보기
      /** Notifies when GameplayEffectSpec is blocked by an ActiveGameplayEffect due to immunity  */
      DECLARE_MULTICAST_DELEGATE_TwoParams(FImmunityBlockGE, const FGameplayEffectSpec& /*BlockedSpec*/, const FActiveGameplayEffect* /*ImmunityGameplayEffect*/);
      
      /** Immunity notification support */
      FImmunityBlockGE OnImmunityBlockGameplayEffectDelegate;

GarantedApplicationImmunityTags

  • 5.3 버전에서 Deprecated 됨
  • UImmunityGameplayEffectComponent를 대신 사용

GrantedApplicationImmunityQuery

  • 5.3 버전에서 Deprecated 됨
  • UImmunityGameplayEffectComponent를 대신 사용

GameplayEffectSpec

  • GE의 인스턴스화로 취급할 수 있다.(이하 GESpec)
    • 자신이 가르키는 GE의 Reference 만들어진 Level, 만든 주체에 대한 정보를 내포하고 있다.
    • 이 정보들은 GE와 다르게 Runtime상에서 적용되기 전까지 자유롭게 생성/수정이 가능하다.
  • GE가 적용 될 때 GESpec이 GE로부터 생성되고 실질적으로 GESpec이 Target에 적용된다.
  • GESpec은 UASC::MakeOutgoingSpec()함수를 통해 GE로부터 생성된다.
    • 더보기
      /** Get an outgoing GameplayEffectSpec that is ready to be applied to other things. */
      UFUNCTION(BlueprintCallable, Category = GameplayEffects)
      virtual FGameplayEffectSpecHandle MakeOutgoingSpec(TSubclassOf<UGameplayEffect> GameplayEffectClass, float Level, FGameplayEffectContextHandle Context) const;
  • GESpec은 그 즉시 적용될 필요가 없다.
    • 보통은 투사체에 GESpec을 전달하고 충돌 발생 시 연산에 사용하곤 한다.
  • GESpec이 성공적으로 적용되면 FActiveGameplayEffect를 반환한다.
  • GESpec에는 아래 내용들이 제공된다.
    • GESpec을 만드는데 사용된 GE 클래스
    • GESpec의 Level
      • 보통 GESpec을 만드는데 사용한 Ability와 같지만 다를수도 있음
    • Duration
      • Default는 GE의 Duration이지만 값이 다를 수도 있다.
    • Period
      • Default는 GE의 Period이지만 다를 수도 있다.
    • 현재 GESpec의 Stack Count
      • 상한은 GE의 설정값을 따른다.
    • GameplayEffectContextHandle
      • 이를 통해 누가 이 GeSpec을 만들었는지 인지할 수 있다.
    • GESpec이 생성 되었을 때 Snapshotting으로 인해 Capture되는 Attribute 들
    • DynamicGrantedTag
      • GESpec이 부여 될 때 GameplayTag에 GE가 추가로 부여해야 하는 GameplayEffectSpec
    • DynamicAssetTag
      • GESpec이 GE와 비교해 추가로 가져야 하는 Tag들
    • SetByCaller

SetByCallers

  • GESpec으로 하여금 GameplayTag나 FName과 관련된 float 값을 전달할 수 있도록 한다.
    • 이들은 GESpec에 TMap 형태로 저장된다.
    • 이 값들은 GE의 Modifier에 사용되거나, 일반적인 의미의 Ferring Floats로 사용될 수 있다.

Modifiers에서 사용

  • GE class보다 앞서 정의되어야만 한다.
  • GameTag 버전만 사용 가능하다.
  • GE class에 하나가 정의 되었는데 GESpec이 대응하는 Tag-float pair가 없는 경우,
    GESpec에서 Runtime Error가 발생하며 0을 반환한다.
  • Divide Operation에서 문제를 야기할 수 있다.

그 외에서 사용

  • 어느 class보다도 앞서 정의 될 필요가 없다.
  • GESpec에 존재하지 않는 SetByCaller를 읽더라도 사용자로 하여금 Default 값과 warning을 반환할 수 있다.

관련 함수

  • 보통 Ability 안에서 생성된 숫자 데이터들은 SetByCallers을 통해
    GameplayEffectExecutionCalculations나 ModifierMagnitudeCalculations로 전달하는 것이 일반적이다.
  • SetByCaller를 BP에서 읽을 수 있으려면 BPLibrary에 Custom Node를 만들어 줘야 한다.
  • GESpec은 SetByCaller 값과 관련된 아래 함수들을 제공하고 있다.
    • 더보기
      /** Sets the magnitude of a SetByCaller modifier */
      void SetSetByCallerMagnitude(FName DataName, float Magnitude);
      
      /** Sets the magnitude of a SetByCaller modifier */
      void SetSetByCallerMagnitude(FGameplayTag DataTag, float Magnitude);
      
      /** Returns the magnitude of a SetByCaller modifier. Will return 0.f and Warn if the magnitude has not been set. */
      float GetSetByCallerMagnitude(FName DataName, bool WarnIfNotFound = true, float DefaultIfNotFound = 0.f) const;
      
      /** Returns the magnitude of a SetByCaller modifier. Will return 0.f and Warn if the magnitude has not been set. */
      float GetSetByCallerMagnitude(FGameplayTag DataTag, bool WarnIfNotFound = true, float DefaultIfNotFound = 0.f) const;
       
    • 이 중 FName을 쓰는 것보다 GameplayTag를 사용하는 것을 권장한다.

GameplayEffectContext

  • GEContext는 GESpec의 Instigator와 TargetData를 가지고 있다.
    • 이는 Subclass를 통해 임시 데이터를 다른 곳으로 전달하는 좋은 구조이다.
    • ModifierMagnitudeCalculations, GameplayEffectExecutionCalculations, AS, GameplayCues 등

사용법

  • FGameplayEffectContext를 상속
  • GetScriptStruct(), Duplicate(), NetSerialize() 함수들을 Override
  • 상위 Class(FGameplayEffectContext)처럼 하위 Class의 TStructOpsTypeTraits를 구현
  • AbilitySystemGlobals::AllocGameplayEffectContext() 함수에서 새로 만든 Class를 반환하도록 override .

ModifierMagnitudeCalculation

  • GE에서 Modifier와 같이 매우 강력한 Class(이하 MMC)
  • 기능은 GameplayEffectExecution Calculations와 비슷하나 덜 강력하고, Predict 가능하다.
    • 유일한 목적은 CalculateBaseMagnitude_Implementation() 함수를 통해 float value를 반환하는 것이다.
  • BP와 C++ 모두에게서 상속 받아 override 할 수 있다.
  • Instant, Duration, Infinite, Periodic GE 모두에게서 사용할 수 있다.
  • MMC의 강점은 GESpec으로부터 GameplayTag나 SetByCaller를 읽을 수 있는 권한을 통해
    Source의 Attribute나 GE의 Target을 캡쳐 할 수 있다는 점이다.
  • 또한 Attribute는 스냅샷이 될 수도 있다.
    • 스냅샷이 되는 Attribute는 GESpec이 생성될 때 캡쳐가 된다.
    • 스냅샷이 되지 않는 Attribute는 Infinite, Duration GE로 인해 자동으로 갱신되어 GESpec이 적용될 때 캡쳐된다.
  • Attribute를 캡쳐하는 것은 ASC에 존재한 mod로부터 CurrentValue를 재계산 한다.
    • 이 재계산은 AbilitySet의 PreAttributeChange() 함수를 호출하지 않기 때문에
      그 과정 속에서 Clamping이 이루어져야 한다.
  • 만약 Duration, Infinite GE에서 Modifier를 다시 계산해야 한다면, SetActiveGameplayEffectLevel 함수를 사용한다.
    • 더보기
      void SetActiveGameplayEffectLevel(FActiveGameplayEffectHandle ActiveHandle, int32 NewLevel);
  • MMC의 결과값은 계수와 계수 전후 추가 값으로 인해 GE의 Modifier에서 수정될 수 있다.

GameplayEffectExecutionCalculation

  • GE로 하여금 ASC에 변화를 가할 수 있는 가장 강력한 방법(이하 ExeCalc)
    • MMC와 같이 Attribute를 캡쳐하고 선택적으로 스냅샷 할 수 있다.
    • MMC와 다르게 1개 이상의 Attribute를 바꿀 수 있고, 무엇보다 Programmer가 원하는 것을 추가할 수 있다.
    • 단점으로는 Predict 할 수 없고 반드시 C++로만 작업이 되어야 한다는 점이다.
  • Instant, Periodic GE에서만 사용할 수 있다.
    • 보통 "Execute"가 붙은 것들은 대부분 Instant, Periodic GE와 관련되어 있다.
  • 스냅샷은 GE가 생성될 때 Attribute를 캡쳐한다.
    • 스냅샷을 하지 않는 것들은 GE가 적용 될 때 Attribute를 캡쳐한다.
  • Attribute를 캡쳐하는 것은 ASC의 mod에 있는 Attribute의 CurrentValue를 재계산한다.
    • 이 재계산은 AbilitySet의 PreAttributeChange 함수를 호출하지 않는다.
    • 때문에 이 과정에서 Clamping이 반드시 이루어져야 한다.
  • Attribute 캡쳐를 설정하기 위해 우리는 Epic의 ActionRPG Sample Project에서 설정한 패턴을 따른다.
    • 이는 Attribute를 저장하고 캡처 방식을 정의하는 구조체를 정의하고, 생성자에서 그 복사본을 생성한다.
    • 또한 각 ExecCalc당 하나씩 있다.
    • 각 구조체는 같은 namespace를 공유하려면 유니크한 이름이 필요하다.
    • 같은 이름의 구조체를 사용하는 것은 Attribute Capture에 있어 모호함을 발생한다.
  • Local Predicted, Server Only, Server Initiated GameplayAbility에서 ExecCalc는 서버에서만 호출된다.
  • Source와 Target의 여러 Attribute로부터 복잡한 연산을 통해 Damage를 계산하는 것이
    가장 대표적인 ExecCalc의 예시이다.

ExeCalc에 Data 보내기

  • Attribute Capture에 더불어 ExeCalc에 정보를 전달하는 몇 가지 방법이 있다.

SetByCaller

  • GESpec에 설정된 SetByCaller는 ExeCalc가 직접 읽을 수 있디ㅏ.
  • 더보기
    const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
    float Damage = FMath::Max<float>(Spec.GetSetByCallerMagnitude(FGameplayTag::RequestGameplayTag(FName("Data.Damage")), false, -1.0f), 0.0f);

Attribute Calculation Modifier 데이터 백업

  • 만약 값을 GE에 하드코딩 하고 싶다면,
    백업 데이터로 캡쳐한 Attribute 중 하나를 CalculationModifier를 통해 전달할 수 있다.

Temporary Variable Calculation Modifier 데이터 백업

  • 만약 값을 GE에 하드코딩 하고 싶다면,
    C++에서 호출되는 Temporary Variable이나 Transient Aggregator를 CalculationModifier를 통해 전달할 수 있다.
  • Temporary Variable은 GameplayTag와 연관이 있다.

Gameplay Effect Context

  • GESpec의 Custom GameplayEffectContext를 통해 ExecutionCalculation으로 데이터를 보낼 수 있다.

CustomApplicationRequirement

  • Designer로 하여금 GE의 적용여부를 통제할 수 있는 고급 기능을 제공한다.(이하 CAR)
    • 단순히 GameplayTag 검사보다 GE에 대해 더 복잡한 조건을 설정할 수 있게 한다.
  • C++에서는 CanApplyGameplayEffect_Implementation(), BP에서는 CanApplyGameplayEffect를 override 할 수 있다.
  • 또한 CAR는 좀 더 확장된 기능을 작업할 수 있다.
    • GE Instance가 이미 Target에 있는지 여부 확인
    • 새 Instance를 등록하지 않고 기존 Instance의 Duration을 변경

Executions

더보기
/** 
 * Struct representing the definition of a custom execution for a gameplay effect.
 * Custom executions run special logic from an outside class each time the gameplay effect executes.
 */
USTRUCT(BlueprintType)
struct GAMEPLAYABILITIES_API FGameplayEffectExecutionDefinition
{
	GENERATED_USTRUCT_BODY()

	/**
	 * Gathers and populates the specified array with the capture definitions that the execution would like in order
	 * to perform its custom calculation. Up to the individual execution calculation to handle if some of them are missing
	 * or not.
	 * 
	 * @param OutCaptureDefs	[OUT] Capture definitions requested by the execution
	 */
	void GetAttributeCaptureDefinitions(OUT TArray<FGameplayEffectAttributeCaptureDefinition>& OutCaptureDefs) const;

	/** Custom execution calculation class to run when the gameplay effect executes */
	UPROPERTY(EditDefaultsOnly, Category=Execution)
	TSubclassOf<UGameplayEffectExecutionCalculation> CalculationClass;
	
	/** These tags are passed into the execution as is, and may be used to do conditional logic */
	UPROPERTY(EditDefaultsOnly, Category = Execution)
	FGameplayTagContainer PassedInTags;

	/** Modifiers that are applied "in place" during the execution calculation */
	UPROPERTY(EditDefaultsOnly, Category = Execution)
	TArray<FGameplayEffectExecutionScopedModifierInfo> CalculationModifiers;

	/** Other Gameplay Effects that will be applied to the target of this execution if the execution is successful. Note if no execution class is selected, these will always apply. */
	UPROPERTY(EditDefaultsOnly, Category = Execution)
	TArray<FConditionalGameplayEffect> ConditionalGameplayEffects;
};


	/** Array of executions that will affect the target of this effect */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = GameplayEffect)
	TArray<FGameplayEffectExecutionDefinition> Executions;
  • Modifier에서 지원하는 정보 이상의 것이 필요할 때에는 사용
  • UGameplayEffectExecutionCalculation을 사용하면 GE의 Custom Behaviour를 정의
    • Modifier로 커버할 수 없는 복잡한 식을 정의할 때 유용

Application Requirements

  • GE를 적용하는 조건을 처리하는 부분
  • 5.3 버전 기준으로 Deprecated
    • UCustomCanApplyGameplayEffectComponent를 대신 사용

Granted Abilities

  • GE를 통해 부여하는 Tag 혹은 Attribute
  • 5.3 버전 기준으로 Deprecated
    • GetBlockedAbilityTags 함수를 통해 UTargetTagsGameplayEffectComponent를 대신 사용

Overflow Effects 

	/** Effects to apply when a stacking effect "overflows" its stack count through another attempted application. Added whether the overflow application succeeds or not. */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category="Stacking|Overflow", meta = (EditConditionHides, EditCondition = "StackingType != EGameplayEffectStackingType::None"))
	TArray<TSubclassOf<UGameplayEffect>> OverflowEffects;
  • 이미 적용된 GE가 다시 적용되는 Overflow 상황을 처리하는 정책
    • 이미 적용되어 있는 GE가 포화상태가 되면 새로운 GE가 적용되는 것을 지칭
  • System상 다양한 Behaviour를 제공
    • 한계치를 넘을 때까지 중첩
    • 새로 적용될 때마다 늘어나는 Stack 수를 최대 한도까지 유지
    • 시간이 지나명 GE의 시간이 리셋 또는 추가
    • 단순한 GE의 여러 Instance에 개별 Timer를 독립적으로 적용

Gameplay Cue Display

더보기
/**
 * FGameplayEffectCue
 *	This is a cosmetic cue that can be tied to a UGameplayEffect. 
 *  This is essentially a GameplayTag + a Min/Max level range that is used to map the level of a GameplayEffect to a normalized value used by the GameplayCue system.
 */
USTRUCT(BlueprintType)
struct FGameplayEffectCue
{
	GENERATED_USTRUCT_BODY()

	FGameplayEffectCue()
		: MinLevel(0.f)
		, MaxLevel(0.f)
	{
	}

	FGameplayEffectCue(const FGameplayTag& InTag, float InMinLevel, float InMaxLevel)
		: MinLevel(InMinLevel)
		, MaxLevel(InMaxLevel)
	{
		GameplayCueTags.AddTag(InTag);
	}

	/** The attribute to use as the source for cue magnitude. If none use level */
	UPROPERTY(EditDefaultsOnly, Category = GameplayCue)
	FGameplayAttribute MagnitudeAttribute;

	/** The minimum level that this Cue supports */
	UPROPERTY(EditDefaultsOnly, Category = GameplayCue)
	float	MinLevel;

	/** The maximum level that this Cue supports */
	UPROPERTY(EditDefaultsOnly, Category = GameplayCue)
	float	MaxLevel;

	/** Tags passed to the gameplay cue handler when this cue is activated */
	UPROPERTY(EditDefaultsOnly, Category = GameplayCue, meta = (Categories="GameplayCue"))
	FGameplayTagContainer GameplayCueTags;

	float NormalizeLevel(float InLevel)
	{
		float Range = MaxLevel - MinLevel;
		if (Range <= KINDA_SMALL_NUMBER)
		{
			return 1.f;
		}

		return FMath::Clamp((InLevel - MinLevel) / Range, 0.f, 1.0f);
	}
};


	/** Cues to trigger non-simulated reactions in response to this GameplayEffect such as sounds, particle effects, etc */
	UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "GameplayCues")
	TArray<FGameplayEffectCue>	GameplayCues;
  • Particle이나 Sound 같은 Decoration Effect를 관리하는 효율적인 Network 방식
    • GAS로 제어할 수 있다.
  • GA나 GE로 트리거 할 수 있다.
  • 모든 Gameplay Cue에는 GameplayCue로 시작하는 Gameplay Tag를 붙여야 한다.
  • Native C++ 혹은 BP로 Override할 수 있고 4개 함수로 작동한다.
    • 더보기
      	/** Called when a GameplayCue is executed, this is used for instant effects or periodic ticks */
      	UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify")
      	bool OnExecute(AActor* MyTarget, const FGameplayCueParameters& Parameters);
      
      	/** Called when a GameplayCue with duration is first activated, this will only be called if the client witnessed the activation */
      	UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify")
      	bool OnActive(AActor* MyTarget, const FGameplayCueParameters& Parameters);
      
      	/** Called when a GameplayCue with duration is first seen as active, even if it wasn't actually just applied (Join in progress, etc) */
      	UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify")
      	bool WhileActive(AActor* MyTarget, const FGameplayCueParameters& Parameters);
      
      	/** Called when a GameplayCue with duration is removed */
      	UFUNCTION(BlueprintNativeEvent, Category = "GameplayCueNotify")
      	bool OnRemove(AActor* MyTarget, const FGameplayCueParameters& Parameters);

GA 와의 Interaction

  • GE가 GA와 상호작용할 때에 AS에서 제공하는 함수를 이용한다.
    • 하지만 이는 기본적인 기능만 제공하고,
      Attribute의 값 변경에 대한 추가 작업이나 범위 지정을 하려면 AS의 함수를 Override 해야 한다.
  •  
  • 더보기
    	/**
    	 *	Called just before modifying the value of an attribute. AttributeSet can make additional modifications here. Return true to continue, or false to throw out the modification.
    	 *	Note this is only called during an 'execute'. E.g., a modification to the 'base value' of an attribute. It is not called during an application of a GameplayEffect, such as a 5 ssecond +10 movement speed buff.
    	 */	
    	virtual bool PreGameplayEffectExecute(struct FGameplayEffectModCallbackData &Data) { return true; }
    	
    	/**
    	 *	Called just after a GameplayEffect is executed to modify the base value of an attribute. No more changes can be made.
    	 *	Note this is only called during an 'execute'. E.g., a modification to the 'base value' of an attribute. It is not called during an application of a GameplayEffect, such as a 5 ssecond +10 movement speed buff.
    	 */
    	virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData &Data) { }
    
    	/**
    	 *	An "On Aggregator Change" type of event could go here, and that could be called when active gameplay effects are added or removed to an attribute aggregator.
    	 *	It is difficult to give all the information in these cases though - aggregators can change for many reasons: being added, being removed, being modified, having a modifier change, immunity, stacking rules, etc.
    	 */
    
    	/**
    	 *	Called just before any modification happens to an attribute. This is lower level than PreAttributeModify/PostAttribute modify.
    	 *	There is no additional context provided here since anything can trigger this. Executed effects, duration based effects, effects being removed, immunity being applied, stacking rules changing, etc.
    	 *	This function is meant to enforce things like "Health = Clamp(Health, 0, MaxHealth)" and NOT things like "trigger this extra thing if damage is applied, etc".
    	 *	
    	 *	NewValue is a mutable reference so you are able to clamp the newly applied value as well.
    	 */
    	virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { }
    
    	/** Called just after any modification happens to an attribute. */
    	virtual void PostAttributeChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) { }
    
    	/**
    	 *	This is called just before any modification happens to an attribute's base value when an attribute aggregator exists.
    	 *	This function should enforce clamping (presuming you wish to clamp the base value along with the final value in PreAttributeChange)
    	 *	This function should NOT invoke gameplay related events or callbacks. Do those in PreAttributeChange() which will be called prior to the
    	 *	final value of the attribute actually changing.
    	 */
    	virtual void PreAttributeBaseChange(const FGameplayAttribute& Attribute, float& NewValue) const { }
    
    	/** Called just after any modification happens to an attribute's base value when an attribute aggregator exists. */
    	virtual void PostAttributeBaseChange(const FGameplayAttribute& Attribute, float OldValue, float NewValue) const { }

 

'UE5 > GAS' 카테고리의 다른 글

[GAS] Gameplay Ability  (0) 2024.05.17
[GAS] Ability Task  (0) 2024.05.16
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Ability System Component  (0) 2024.05.13

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-attributes-and-attribute-sets-for-the-gameplay-ability-system-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템을 위한 게임플레이 어트리뷰트 및 어트리뷰트 세트

게임플레이 어트리뷰트 및 어트리뷰트 세트 사용하기

dev.epicgames.com

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-as

 

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. - tranek/GASDocumentation

github.com

Definition

  • 하나 이상의 GA로 AS를 구성하고, ASC에 등록을 한다.
    • Owner Actor의 Constructor에서 AttributeSet을 생성하면 ASC에 자동으로 등록이 된다.

Design

  • ASC가 여러 개의 AS를 가질 수는 있지만, 각 AS는 모두 서로 클래스가 달라야 한다.
  • AS는 무시할만한 Memory Overhead를 야기한다.
    • 그렇기에 얼마나 AS를 많이 사용할지는 개발자의 결정에 달려있다.
  • 또한 AS는 Actor가 어떤 Attribute를 가질지 선택지를 제공하는 의미로 SubClass를 생성할 수 있다.
    • Atribute는 내부에서 AttributeSetClassName.AttributeName으로 일컬어진다.
    • 때문에 AS를 상속 받더라도, 상위 AS의 Attribute는 상위 AS의 이름을 그대로 유지한다.

Subcomponents with Individual Attributes

  • 장비 파괴처럼 복수의 Damagable Component를 소유했을 때, Component의 최대 갯수를 알 수 있다면
    AttributeSet에 각 Component에 대응하는 health Attribute를 만드는 것을 권장한다.
    • Damagable Component에는 가상의 Slot 값을 부여
    • 부여된 Slot값을 통해 GameplayAbilities를 읽거나 Attribute에 접근해 데미지 계산이 가능
    • Character/Pawn은 1개 이하의 health Attribute만 소유하는 것을 권장
    • Attribute는 AttributeSet이 가져야 하는거지, Character/Pawn이 가져야만 하는 것이 아니기 때문.
  • 하지만 다음 조건에서는 위와 같은 방식이 기대한 것만큼 잘 동작하지 않는다.
    • Subcomponent가 Runtime 상 상한이 없는 경우
    • Subcomponent가 Detach되어 다른 유저가 사용할 수 있는 경우
  • 이 때에는 Attribute를 사용하지 말고 고전적인 float 타입의 변수를 Component에 직접 부여하는 것을 권장한다.
  • 자세한 사항은 Item Attribute를 확인하길 바란다.

Add/Remove AttributeSet at Runtime

  • AS은 Runtime 상에 ASC에 추가될 수 있다.
    • 물론 ASC로부터 제거도 가능하지만 이는 매우 위험하다.
    • 만약 client에서 server보다 먼저 AS를 제거하고 Attribute 값 변화가 client로 replicate 된다면, 
      Attribute를 찾을 수 없어 Crash가 난다.
  • 그렇기에 Runtime 상에서 AS를 추가/제거를 할 때에는 대상 ASC에 대해 ForceReplication 함수를 호출해줘야 한다.

Item Attribute

  • 장비류 아이템에 대한 Attribute를 구현하는 방식.
    • 이런 방식은 전부 Item에 직접 값을 저장한다.
  • 이 시스템은 1명 이상의 플레이어가 Lifetime동안 장비를 장착/탈착 할 수 있는 경우에 반드시 제공되어야 한다.

Plain Floats on the Item

  • Attribute 대신 float 변수로 정보를 관리하는 방식
    • Fortnite와 GAS 예시코드가 이런 방식을 채택
  • 총기가 최대 Ammo 수, 현재 Ammo 수, 남은 Ammo 수 등을 저장하고 Client에 Replicate한다.
  • 만약 총기가 남은 Ammo를 공유하며느 그 값을 Character의 Attribute로 가져와 AttributeSet에 저장한다.
  • 다만 총기에서 Attribute를 사용하지 않기 때문에, UGameplayAbility에서 제공하는 일부 함수를 override해야 한다.
    • 총기에서 사용하는 Plain Float에 대한 검증과 계산을 위해
  • 총기를 GameplayAbilitySpec의 SourceObject로 지정하면 접근 권한을 Ability 내에서 처리할 수 있다.
  • 자동사격 과정에서 총기의 Ammo가 Replicate로 인해 덮어씌어지는 Clobbering을 방지하기 위해,
    PreReplication() 함수에서 IsFiring GameplayTag가 있는 동안 Ammo의 Replicate를 비활성화 한다.
    • 더보기
      void AGSWeapon::PreReplication(IRepChangedPropertyTracker& ChangedPropertyTracker)
      {
      	Super::PreReplication(ChangedPropertyTracker);
      
      	DOREPLIFETIME_ACTIVE_OVERRIDE(AGSWeapon, PrimaryClipAmmo, (IsValid(AbilitySystemComponent) && !AbilitySystemComponent->HasMatchingGameplayTag(WeaponIsFiringTag)));
      	DOREPLIFETIME_ACTIVE_OVERRIDE(AGSWeapon, SecondaryClipAmmo, (IsValid(AbilitySystemComponent) && !AbilitySystemComponent->HasMatchingGameplayTag(WeaponIsFiringTag)));
      }
    • 장점
      • AttributeSet을 사용하면서 발생하는 한계를 피할 수 있음
    • 단점
      • 현존하는 GameplayEffects의 흐름을 사용할 수 없음.
      • UGameplayAbility의 핵심 함수를 ammo 관리를 위해 override 해야 함.

AttributeSet on the Item

  • Item에서 AttributeSet을 사용하고 Player의 ASC를 이용하는 방법
  • 하지만 근본적인 한계가 존재한다.
    • Weapon이 가지고 있는 고유한 Attribute를 경우에 따라 Character로 옮겨와야 한다.
    • 만약 Weapon을 Inventory에 넣는다면,
      Weapon의 AttributeSet을 Character가 가진 ASC의 SpawnedAttributes에 옮겨와야 한다.
  • 만약 AttributeSet이 OwnerActor가 아닌 곳에 존재하면 Compile Error가 발생한다.
  • 이를 수정하기 위해 다음 작업이 진행되어야 한다.
    • AttributeSet을 Constructor가 아닌 BeginPlay()에서 Construct
    • IAbilitySystemInterface를 Implement
  • 장점
    • 제공되는 GameplayAbility와 GameplayEffect를 그대로 사용 가능
    • Item에 간단한 세팅으로 기능 구현 가능
  • 단점
    • 각 무기 타입에 맞는 AttributeSet을 생성해야 한다.
      • ASC는 기능적으로 Class당 하나의 AttributeSet을 가질 수 밖에 없다.
      • ASC가 Attribute 변경사항을 적용할 때 SpawnedAttributes에서 조건에 맞는 첫번째 AS만 탐색하기 때문.
      • 나머지 AttributeSet은 항상 무시 됨
    • 위와 같은 이유로 플레이어 당 동일한 무기를 2개 이상 소지할 수 없음
    • AttributeSet을 Runtime상에서 제거하는 것에서 오는 근본적인 리스크
      • 예를 들어 탄속이 있는 무기로 적을 처치 했는데 발사와 처치 사이에 무기가 제거가 되면
        관련 AttributeSet을 찾지 못해 Crash가 발생한다.

ASC on the Item

  • ASC를 통째로 아이템에 박아 넣는 극단적인 방법.
  • 장점
    • GameplayAbility와 GameplayEffect의 모든 기능을 오롯이 사용 가능
    • Attributeset Class를 재사용 할 수 있음
      • ASC에서 1개만 존재하니까
  • 단점
    • 개발 비용이 까마득하고 검증되지 않음

Defining Attributes

  • 더보기
     UCLASS()
     class MYPROJECT_API UMyAttributeSet : public UAttributeSet
     {
         GENERATED_BODY()
     
         public:
         /** 퍼블릭 액세스 가능한 샘플 어트리뷰트 'Health'*/
         UPROPERTY(EditAnywhere, BlueprintReadOnly)
         FGameplayAttributeData Health;
     };

     

    class MyAbilitySystemComponent : public UAbilitySystemComponent
    {
    	....
    	/** 샘플 어트리뷰트 세트. */
    	UPROPERTY()
    	const UMyAttributeSet* AttributeSet;
    	....
    }
     // 이런 코드는 일반적으로 BeginPlay()에 나타나지만,
     // 적절한 어빌리티 시스템 컴포넌트 구하기일 수 있습니다. 다른 액터에 있을 수도 있으므로 GetAbilitySystemComponent를 사용하여 결과가 유효한지 확인하세요.
     AbilitySystemComponent* ASC = GetAbilitySystemComponent();
     // AbilitySystemComponent가 유효한지 확인합니다. 실패를 허용할 수 없다면 if() 조건문을 check() 구문으로 대체합니다.
     if (IsValid(ASC))
     {
         // 어빌리티 시스템 컴포넌트에서 UMyAttributeSet를 구합니다. 필요한 경우 어빌리티 시스템 컴포넌트가 UMyAttributeSet를 생성하고 등록할 것입니다.
         AttributeSet = ASC->GetSet<UMyAttributeSet>();
     
         // 이제 새 UMyAttributeSet에 대한 포인터가 생겨 나중에 사용할 수 있습니다. 초기화 함수가 있는 경우 여기서 호출하면 좋습니다.
     }


    1.  Attribute는 FGameplayAttributeData 타입으로 Attribute를 선언한다.
    2. 위 코드에서는 Attribute Set 안의 값이 코드에서 직접 수정하지 않도록 const로 선언을 정의한다.
    3. AttributeSet은 Actor Construct에서 Instance화 하는 시점에 함수가 유효한 ASC를 반환하는 한,
      이 시점 혹은 BeginPlay 도중에 자동으로 등록된다.
    4. BP를 편집하여 Attribute Set Type을 ASC의 Default Start Data로 추가할 수 있다.
    5. ASC가 없는 GA를 수정하는 Gameplay Effect가 적용된다면, ASC는 일치하는 GA를 자동으로 생성한다.
      1. 하지만 이 Methond는 AS를 생성하거나, 기존 AS에 GA를 추가하지 않는다.

Helper Funcion Macro

  • GAS에서는 GA와 상호작용할 기본 헬퍼 함수를 추가할 수 있다.
    • GA 자체는 protected/private 접근자로 설정하고, 상호작용 함수를 public으로 설정하는 것이 좋다.
  • 사용이 필수 사항은 아니지만, 모범 사례로 간주한다.
  • 더보기
    #define GAMEPLAYATTRIBUTE_REPNOTIFY(ClassName, PropertyName, OldValue) \
    { \
    	static FProperty* ThisProperty = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
    	GetOwningAbilitySystemComponentChecked()->SetBaseAttributeValueFromReplication(FGameplayAttribute(ThisProperty), PropertyName, OldValue); \
    }
    
    #define GAMEPLAYATTRIBUTE_PROPERTY_GETTER(ClassName, PropertyName) \
    	static FGameplayAttribute Get##PropertyName##Attribute() \
    	{ \
    		static FProperty* Prop = FindFieldChecked<FProperty>(ClassName::StaticClass(), GET_MEMBER_NAME_CHECKED(ClassName, PropertyName)); \
    		return Prop; \
    	}
    
    #define GAMEPLAYATTRIBUTE_VALUE_GETTER(PropertyName) \
    	FORCEINLINE float Get##PropertyName() const \
    	{ \
    		return PropertyName.GetCurrentValue(); \
    	}
    
    #define GAMEPLAYATTRIBUTE_VALUE_SETTER(PropertyName) \
    	FORCEINLINE void Set##PropertyName(float NewVal) \
    	{ \
    		UAbilitySystemComponent* AbilityComp = GetOwningAbilitySystemComponent(); \
    		if (ensure(AbilityComp)) \
    		{ \
    			AbilityComp->SetNumericAttributeBase(Get##PropertyName##Attribute(), NewVal); \
    		}; \
    	}
    
    #define GAMEPLAYATTRIBUTE_VALUE_INITTER(PropertyName) \
    	FORCEINLINE void Init##PropertyName(float NewVal) \
    	{ \
    		PropertyName.SetBaseValue(NewVal); \
    		PropertyName.SetCurrentValue(NewVal); \
    	}

Initialize

  • AttributeMetaData라는 GAS TableRow로 초기화 할 수 있다.
    • 이는 외부 파일에서 Import할 수도 있고, Editor에서 수동으로 채워줄 수도 있다.
  •  
    DataTable Asset 생성 할 때 AttributeMetaData를 선택한다.
  • 위와 같이 선언된 AttributeMetaData Table Asset에 추가 Row를 덧붙여 AS를 다수의 GA로 지원할 수 있다.
    • 예를 들어 MyAttributeSet.Health 외에 MyAttributeSet.Attack Row를 추가하면 한 AS에서 제공하는 다수의 GA를 하나의 Table Asset으로 Initialize 할 수 있다.
  • Column에 MinValue, MaxValue가 지원되지만 GA와 AS에 범위 제한 행동이 없으므로 이 항목들은 무효하다.

Gameplay Attribute Access 제어

  • GA에 대한 직접 Access를 지어하는 것은 AS 통해 수행되며, FGameplayAttributeData를 확장하지 않는다.
    • FGameplayAttributeData는 Data에 대한 Access만 저장하고 제공한다.
  • GAS에서 제공하는 Macro를 사용하지 않고, 동일한 Macro에 구현부를 직접 작업해 값의 범위를 제한할 수도 있다.
  • 더보기
    GAMEPLAYATTRIBUTE_PROPERTY_GETTER(UMyAttributeSet, Health);
    float GetHealth() const;
    void SetHealth(float NewVal);
    GAMEPLAYATTRIBUTE_VALUE_INITTER(Health);

     

    float UMyAttributeSet::GetHealth() const
    {
        // Health의 현재 값을 반환하지만, 0 미만의 값은 반환하지 않습니다.
        // 이 값은 Health에 영향을 미치는 모든 모디파이어가 고려된 후의 값입니다.
        return FMath::Max(Health.GetCurrentValue(), 0.0f);
    }
    
    void UMyAttributeSet::SetHealth(float NewVal)
    {
        // 0 미만의 값을 받지 않습니다.
        NewVal = FMath::Max(NewVal, 0.0f);
    
        // 어빌리티 시스템 컴포넌트 인스턴스가 있는지 확인합니다. 항상 인스턴스가 있어야 합니다.
        UAbilitySystemComponent* ASC = GetOwningAbilitySystemComponent();
        if (ensure(ASC))
        {
            // 적절한 함수를 통해 현재 값이 아닌 베이스 값을 설정합니다.
            // 그러면 적용한 모디파이어가 계속해서 적절히 작동합니다.
            ASC->SetNumericAttributeBase(GetHealthAttribute(), NewVal);
        }
    }
    
    AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSet->GetHealthAttribute()).AddUObject(this, &AGASAbilityDemoCharacter::OnHealthChangedInternal);

Gameplay Effect와의 Interaction

  • GA의 값을 제어하는 가장 일반적인 방법은 관련 Gameplay Effect를 처리하는 것이다.
  •  
    • PostGameplayEffectExecute는 Attribute의 Base 값을 변경되는 GameplayEffect가 호출되기 전에 호출된다.
      • GameplayEffect가 호출(Execute) 되기 전에만 호출되고, 직접 접근해 수정할 때에는 호출되지 않는다.
      • 또한 BaseValue가 아닌 current 값에 영향을 주는 GameplayEffect에서도 호출되지 않는다.
    • 위 예시에서 PostGameplayEffectExecute 함수는 public 접근자가 지정되어야 한다.
    • 함수 내에서 새로운 Body를 작성하되, Super::PostGameplayEffectExecute가 확실히 호출되어 있어야 한다.
  • 더보기
    class UMyAttributeSet : public AttributeSet
    {
    	.....
    public:
    	void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data) override;
    	.....
    }

     

    void UMyAttributeSet::PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData& Data)
    {
    	// 잊지 말고 부모 구현을 호출하세요.
    	Super::PostGameplayEffectExecute(Data);
    
    	// 프로퍼티 게터를 사용하여 이 호출이 Health에 영향을 미치는지 확인합니다.
    	if (Data.EvaluatedData.Attribute == GetHealthAttribute())
    	{
    		// 이 게임플레이 이펙트는 Health를 변경합니다. 적용하되 우선 값을 제한합니다.
    		// 이 경우 Health 베이스 값은 음수가 아니어야 합니다.
    		SetHealth(FMath::Max(GetHealth(), 0.0f));
    	}
    }

Replication

  • Multiplayer의 경우, 여타 Property Replicate와 유사하게 AS을 통해 GA Replicate를 할 수 있다.
  • 더보기
    class UMyAttributeSet : public UAttributeSet
    {
    	.....
    
        /** 네트워크를 통해 새 Health 값이 도착할 때 호출됨 */
        UFUNCTION()
        virtual void OnRep_Health(const FGameplayAttributeData& OldHealth);
        
        /** 리플리케이트할 프로퍼티 표시 */
        virtual void GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const override;
    
    protected:
        /** 샘플 어트리뷰트 'Health'*/
        UPROPERTY(EditAnywhere, BlueprintReadOnly, ReplicatedUsing = OnRep_Health)
        FGameplayAttributeData Health;
        
        ....
    };

     

    void UMyAttributeSet::OnRep_Health(const FGameplayAttributeData& OldHealth)
    {
    	// 디폴트 게임플레이 어트리뷰트 시스템의 repnotify 행동을 사용합니다.
    	GAMEPLAYATTRIBUTE_REPNOTIFY(UMyAttributeSet, Health, OldHealth);
    }
    
    void UMyAttributeSet::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
    {
    	// 부모 함수를 호출합니다.
             Super::GetLifetimeReplicatedProps(OutLifetimeProps);
    
    	// Health에 대한 리플리케이션을 추가합니다.
    	DOREPLIFETIME_CONDITION_NOTIFY(UMyAttributeSet, Health, COND_None, REPNOTIFY_Always);
    }
    • Replicate 할 Attribute에 PROPERTY에 ReplicatedUsing 지정자를 추가하고, Callback 함수를 선언한다.
    • Source File에서 Callback 함수를 정의한다.
    • Callback 함수 내에는 GAMEPLAYATTRIBUTE_REPNOTIFY 매크로로
      Replicate 되는 Attribute에 대한 RepNotify 행동을 실행한다.
    • 만약 이 AS가 처음 Replicate되는 Attribute라면, GetLifetimeReplicatedProps에 추가한다.

주요 함수

PreAttributeChange

	/**
	 *	Called just before any modification happens to an attribute. This is lower level than PreAttributeModify/PostAttribute modify.
	 *	There is no additional context provided here since anything can trigger this. Executed effects, duration based effects, effects being removed, immunity being applied, stacking rules changing, etc.
	 *	This function is meant to enforce things like "Health = Clamp(Health, 0, MaxHealth)" and NOT things like "trigger this extra thing if damage is applied, etc".
	 *	
	 *	NewValue is a mutable reference so you are able to clamp the newly applied value as well.
	 */
	virtual void PreAttributeChange(const FGameplayAttribute& Attribute, float& NewValue) { }
  • Attribute의 CurrentValue가 바뀌기 전에 호출되는 함수
    • NewValue를 이용해 CurrentValue를 Clamp한 상태로 적용할 때 사용 함.
    • 더보기
      if (Attribute == GetMoveSpeedAttribute())
      {
      	// Cannot slow less than 150 units/s and cannot boost more than 1000 units/s
      	NewValue = FMath::Clamp<float>(NewValue, 150, 1000);
      }
  • 단, Attribute에 직접 접근하여 변경하는 것이 아닌 Macro에서 제공하는 Setter 함수나 GameplayEffects로만 호출된다.
  • 그리고 Epic에서는 이 함수를 Attribute 변경에 대한 Delegate로 사용을 권장하지 않는다.
    • 그저 Attribute 값의 보정(Clamp) 용도로 사용을 권장한다.

PostGameplayEffectExecute

	/**
	 *	Called just after a GameplayEffect is executed to modify the base value of an attribute. No more changes can be made.
	 *	Note this is only called during an 'execute'. E.g., a modification to the 'base value' of an attribute. It is not called during an application of a GameplayEffect, such as a 5 ssecond +10 movement speed buff.
	 */
	virtual void PostGameplayEffectExecute(const struct FGameplayEffectModCallbackData &Data) { }
  • Attribute의 BaseValue가 Instant GameplayEffect로 인해 변경될 때 호출되는 함수
    • GameplayEffect로 인해 Attribute가 변동될 때 좀 더 복잡한 작업을 수행할 수 있다.
  • 함수가 호출된 시점에는 이미 Attribute 값이 변경이 된 후다.
    • 하지만 아직 Client에는 Replicate 되지 않았다.
  • 때문에 여기서 Clamping을 하는 것은 Client에서는 발생하지 않고, 그 결과만 받는다.

OnAttributeAggregatorCreate

	/** Callback for when an FAggregator is created for an attribute in this set. Allows custom setup of FAggregator::EvaluationMetaData */
	virtual void OnAttributeAggregatorCreated(const FGameplayAttribute& Attribute, FAggregator* NewAggregator) const { }
  • Attribute가 set에서 생성되어 Aggregator가 생성될 때.
  • FAggregatorEvaluateMetaData에 대한 Custom Setup을 할 수 있다.
    • Attribute에서 Modifier로 적용하는데 가장 작은 음수 값을 가져올 수 있다.
    • Paragon에서 여러 개의 감속 디버프가 걸릴 때 가장 낮은 비율로 적용할 때 이 기능을 사용
  • 더보기
    void UGSAttributeSetBase::OnAttributeAggregatorCreated(const FGameplayAttribute& Attribute, FAggregator* NewAggregator) const
    {
    	Super::OnAttributeAggregatorCreated(Attribute, NewAggregator);
    
    	if (!NewAggregator)
    	{
    		return;
    	}
    
    	if (Attribute == GetMoveSpeedAttribute())
    	{
    		NewAggregator->EvaluationMetaData = &FAggregatorEvaluateMetaDataLibrary::MostNegativeMod_AllPositiveMods;
    	}
    }

 

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Ability System Component  (0) 2024.05.13
[GAS] Gameplay Ability System 소개  (0) 2024.05.10

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-attributes-and-attribute-sets-for-the-gameplay-ability-system-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템을 위한 게임플레이 어트리뷰트 및 어트리뷰트 세트

게임플레이 어트리뷰트 및 어트리뷰트 세트 사용하기

dev.epicgames.com

https://github.com/tranek/GASDocumentation?tab=readme-ov-file#concepts-a

 

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. - tranek/GASDocumentation

github.com

 

  • FGameplayAttribute를 사용해 Gameplay 관련 소수값을 저장/계산 수정하는 기능
    • 남은 생명력
    • Vehicle의 최고 속력
    • 아이템의 사용 가능 횟수
  • GAS의 Actor는 Gameplay Attribute(이하 GA)는 Attribute Set(이하 AS)에 저장한다.
    • 이는 Attribute를 Actor의 ASC에 등록하고, GA와 시스템의 나머지 부분간의 상호작용 관리에 유리하다.
    • 상호작용에는 범위 제한, 일시적 값 변경을 위한 계산, 영구적 값 변경을 하는 이벤트에 대한 반응이 있다.
  • GA는 Gameplay Effect로만 수정되어야 한다.
    • 그래야 ASC가 변화를 예측할 수 있다.

Gameplay Attribute

  • 생성하려면 우선 Attribute Set을 만들어야 한다.
    • 경우에 따라서는 AS가 없이도 GA가 존재할 수 있다.
    • 보통은 적절한 GA Type을 포함하는 AS가 없는 ASC에 GA가 저장된 경우
    • 하지만 이 케이스는 GA가 ASC와 상호작용하는 그 어떠한 행동도 정의되지 않고 값만 저장한다.
    • 때문에 권장되지 않는 방식이다.
  • 만약 GA를 Editor에 노출하고 싶지 않다면, UPROPERTY 지정자에 Meta=(HideInDetailsView)를 추가하면 된다.
  • GA 값은 CurrentValue와 BaseValue로 구분된다.

CurrentValue

  • BaseValue에 더불어 Gameplay Effects로 발생한 일시적인 변화를 합친 값
  • Duration, Infinite Gameplay Effects에 의해 값이 변경된다.
    • Duration Gameplay Effects: 미리 지정된 시간 동안만 효과가 적용되는 Gameplay Effects
    • Infinite Gameplay Effects: 특정 조건을 만족하기 전까지 계속 효과가 유지되는 Gameplay Effects
  • PreAttributeChange() 함수에서 CurrentValue 값의 Clamping 작업을 적용할 수 있다.

BaseValue

  • Attribute의 영구적인 값
  • Instant 혹은 Periodic Gameplay Effects에 의해 값이 변경된다.
    • Instant Gameplay Effects: 즉시 발동되지만 그 효과가 영구적인 Gameplay Effects
    • Periodic Gameplay Effects: 주기적으로 반복해서 효과가 발동하는 Gameplay Effects
  • 간혹 BaseValue Attribute의 최대값하고 혼동하지만 이는 잘못된 사용법이다.
    • 현재는 Attribute의 최대값은 별도의 Attribute로 분리되어야 한다.
    • FAttributeMetaData에 Min/Max Value Column이 있지만, Epic에서 WIP라고 코멘트 해두었다.
  • PostGameplayEffectExecute()에서 BaseValue 값의 Clamp를 적용할 수 있다.

Meta Attributes

  • Attributes들과 상호작용을 목적으로 일시적인 값을 담고 있는 임시 Attribute를 지칭
    • 예를 들어 damage의 경우, GameplayEffect로 health Attribute에 직접 영향을 주는 대신
      damage라는 Meta Attribute를 임시선언으로 사용할 수 있다.
    • 이 때 Meta Attribute는 health Attribute에 도달하기 전에 여러 변화를 가할 수 있다.
      • GameplayEffectExecutionCalculation 함수를 통해 Buff/Debuff 적용
      • Shield Attribute에 의해 발생하는 damage의 감소
  • Meta Attribute는 Gameplay Effect간의 영속성이 없고 모두에 의해 override될 수 있다.
    • 또한 일반적으로 Replicate 되지 않는다.
  • Meta Attribute는 적용 대상 Attribute의 수치 변화와 그에 따른 상호작용을 논리적으로 분리할 수 있다.
    • Gameplay Effect가 "얼마나 수치를 변경할 것인가?"를 결정한다면,
      Attribute Set은 "수치가 바뀐 뒤 어떻게 반응할 것인가?"를 결정한다.
    • 하지만 상위 Attribute Set에서 제공하는 Attribute를 대상으로 한다 해도,
      하위 Attribute Set의 구성에 따라 실제 변화량과 그 반응은 다를 수 있다.
    • Meta Attribute는 이렇게 다양한 경우에 대해 유연하게 대응할 수 있다.
  • 물론 Meta Attribute는 좋은 패턴이지만 필수적인 것은 아니다.
    • 만약 Attribute에 대해 하나의 ExecutionCalculation만 제공되고 모든 Actor가 그 Attribute Set을 사용한다면,
      관련된 값들을 직접 수정하는 방식으로 작업하는 것이 더 좋을 수 있다.

Responding to Attribute Changes

  • Attribute의 변화를 UI나 다른 Gameplay에 전달하려면,
    UAbilitySystemComponent::GetGameplayAttributeValueChangeDelegate(FGameplayAttribute Attribute)를 쓴다.
  • 더보기
    typedef TMulticastDelegate_OneParam< void, const FOnAttributeChangeData & > FOnGameplayAttributeValueChange
    
    FOnGameplayAttributeValueChange & GetGameplayAttributeValueChangeDelegate
    (
        FGameplayAttribute Attribute
    )

     

    AbilitySystemComponent->GetGameplayAttributeValueChangeDelegate(AttributeSetBase->GetHealthAttribute()).AddUObject(this, &AGDPlayerState::HealthChanged);
    
    virtual void HealthChanged(const FOnAttributeChangeData& Data);

     

    • 이 때 Attribute 변수는 Server에서만 값이 채워진다.
  • BP에서는 이 함수가 AsyncTask로 감싸져서 제공된다.
    • 보통 UI_HUD에서 사용되며 해당 Widget의 Destruct Event에서 EndTask()가 호출되기 전까지 죽지 않는다.

Derived Attributes

  • 하나의 Attribute가 복수의 Attribute로부터 값이 정해질 때, Infinite Gameplay Effect를 여러 개의 Modifier Magnitude Calculation(이하 MMC)와 함께 사용한다.
  • Derived Attributes는 해당 Attribute에 의존성을 갖는 Attribute들을 자동으로 갱신해준다.
  • Derived Attribute의 연산자 공식은 Modifier Aggregator와 동일한 방식으로 동작한다.
    • 만약 저해진 순서대로 계산이 되어야 한다면, MMC 내부에서 시행하면 된다.
  • PIE에서 Multi Client로 실행한 경우, Editor Preferences -> Run Under One Process 옵션을 비활성화 해야한다.
    • 안 그러면 Derived Attributes가 각각의 Attribute가 client에서 첫번째로 update되지 않으면 갱신하지 않을 것이다.

예시

  • 다음과 같이 값이 정해지는 TestAttrA가 있다고 가정하자.
    • TestAttrA = (TestAttrA + TestAttrB) * (2 * TestAttrC) .
  • 이 때 TestAttrA에 대한 Derived Attributes 설정은 다음과 같다.
  •  

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Ability System Component  (0) 2024.05.13
[GAS] Gameplay Ability System 소개  (0) 2024.05.10

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-ability-system-component-and-gameplay-attributes-in-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템 컴포넌트 및 게임플레이 어트리뷰트를 살펴봅니다.

게임플레이 어트리뷰트 및 어트리뷰트 세트와 함께 어빌리티 시스템 컴포넌트를 사용하는 방법을 살펴봅니다.

dev.epicgames.com

  • UGameAbilitySystemComponent는 Actor와 GAS 사이의 가교 역할을 한다.
  • GAS와 상호작용해야 하는 Actor는 다음 2가지 조건 중 하나를 반드시 만족해야 한다.
    • Ability System Component(이하 ASC)를 가지고 있거나
    • 다른 Actor의 ASC에 Access 할 수 있거나.
  • ASC는 FGameplayAbilitySpecContainer ActivatableAbilities 중 승인된 Gameplay Abilities를 가지고 있다.
  • 언제나 ActivatableAbilities.Items를 통해 iterate를 하려 한다면,
    List의 변화를 막기 위해 ABILITY_SCOPE_LOCK()을 반복문 앞에 추가해 Lock을 걸어야 한다.
  • 매 ABILITY_SCOPE_LOCK() 호출 범위마다 AbilityScopeLockCount가 증가하고,
    그 범위를 벗어나면 AbilityScopeLockCount가 감소한다.
  • ABILITY_SCOPE_LOCK() 호출 범위 내에서 Ability를 제거하지 말 것.
    • List가 Lock이 걸려 있으면 AbilityScopeLockCount를 확인하여 제거를 방지한다.

기본 요구사항

  • Actor가 Game Ability System를 사용하려면 다음 2가지를 충족해야 한다.
    • IAbilitySystemInterface 구현
    • GetAbilitySystemComponent 함수 override
  • Actor가 자체 ASC를 가지고 있는 것이 일반적이다.
  • 하지만 다른 Actor가 소유한 ASC를 사용하려는 경우가 있다.
    • Player의 Pawn/Character가 소환/소멸 되거나 처리할 때 리셋되면 안되는 것들이 포함되어 있기 때문.

Replication Mode

  • ASC는 GameplayEffects, GameplayTags, GameplayCues에 대해 3가지 Replication Mode를 제공한다.
  • Attributes는 속한 AttributeSet에 의해 Replicate된다.

Full

  • Single Player에서 사용
  • 모든 GameplayEffects가 모든 Client에 Replicate된다.

Mixed

  • Multiplayer에서 유저가 Control 하는 Actor
  • GameplayEffects가 소유한 Client에게만 Replicate된다.
  • GameplayTags와 GameplayCues만 모두에게 Replicate된다.

Advanced

  • 이 Mode에서 OwnerActor의 Owner가 Controller가 되는 것을 권장한다.
    • PlayerState의 Parent는 기본적으로 Controller이지만, Character는 그렇지 않다.
    • OwnerActor가 PlayerState가 아닌 상태에서 Mixed Mode를 사용하는 경우, 
      SetOwner() 함수를 호출해 OwnerActor를 Controller로 명시해야 한다.
    • 4.24 버전 이후(현재)에는 PossessedBy() 함수로 Owner를 Controller로 지정한다.

Minimal

  • Multiplayer에서 AI가 Control 하는 Actor
  • GameplayEffects가 어디에도 Replicate 되지 않는다.
  • GameplayTags와 GameplayCues만 모두에게 Replicate 된다.

예시

 

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. - tranek/GASDocumentation

github.com

ASC가 Actor에 있는 경우

  • 더보기
    AGDPlayerState::AGDPlayerState()
    {
    	// Create ability system component, and set it to be explicitly replicated
    	AbilitySystemComponent = CreateDefaultSubobject<UGDAbilitySystemComponent>(TEXT("AbilitySystemComponent"));
    	AbilitySystemComponent->SetIsReplicated(true);
    	//...
    }

     

    class APACharacterBase : public AActor, public IAbilitySystemInterface
    {
    	//~ IAbilitySystemInterface 시작
    	/** 어빌리티 시스템 컴포넌트를 반환합니다. */
    	virtual UAbilitySystemComponent* GetAbilitySystemComponent() const override;
    	//~ IAbilitySystemInterface 끝
       
    	/** 어빌리티 시스템 컴포넌트입니다. 게임플레이 어트리뷰트 및 게임플레이 어빌리티를 사용하려면 필요합니다. */
    	UPROPERTY(VisibleDefaultsOnly, BlueprintReadOnly, Category = "Abilities")
    	UAbilitySystemComponent* AbilitySystemComponent;
    }

     

    void APACharacterBase::PossessedBy(AController * NewController)
    {
    	Super::PossessedBy(NewController);
    
    	if (AbilitySystemComponent)
    	{
    		AbilitySystemComponent->InitAbilityActorInfo(this, this);
    	}
    
    	// ASC MixedMode replication requires that the ASC Owner's Owner be the Controller.
    	SetOwner(NewController);
    }
    
    UAbilitySystemComponent* APACharacterBase::GetAbilitySystemComponent() const
    {
    	return AbilitySystemComponent;
    }

     

    void APAPlayerControllerBase::AcknowledgePossession(APawn* P)
    {
    	Super::AcknowledgePossession(P);
    
    	APACharacterBase* CharacterBase = Cast<APACharacterBase>(P);
    	if (CharacterBase)
    	{
    		CharacterBase->GetAbilitySystemComponent()->InitAbilityActorInfo(CharacterBase, CharacterBase);
    	}
    
    	//...
    }
    • ASC는 Server, Client 양쪽에서 모두 Initialize되어야 한다.
      • 또한 Controller가 Set(Possess)되고 나서 Initialize 되기를 원한다.
      • SinglePlay는 Server 파트에 대해서만 걱정을 하면 된다.
    • ASC가 설정된 Player는 각 파트에서 명시적으로 함수를 호출하여 Initialize를 해야 한다.
      • Server 파트: Pawn::PossessedBy()
      • Client 파트: APlayerController::AcknoledgePossession

고급 시나리오

  • 다른 Actor가 소유한 ASC를 사용하는 Actor를 구성할 수 있다.
    • ex) UPlayerState가 소유한 ASC를 사용하는 Pawn
  • 이를 위해 GetAbilitySystemComponent 함수가 Owner에서 ASC를 얻거나, 캐싱해야 한다.
  • 이 방식은 다음 속성이 있는 프로젝트에서 주로 발생한다.
    • Player가 제어하는 Actor가 리스폰을 하고, 그 과정에서 Game Ability System 정보가 필요한 경우
    • 장비나 Modular Machine, 신체 부위를 표현하기 위해 Actor가 자신에게 다른 Actor를 Attach하는 경우
    • 간단한 AI나 MOBA 게임
  • Attach 된 Actor의 Gameplay Ability System Interaction은 Parent의 ASC에 라우팅 할 수 있다.
    • 가장 간단하게 구현하는 방법은 Attach된 Actor의 GetAbilitySystemComponent가 Parent로 전달되는 것.
    • Actor 다른 ACtor에 Attach되거나 처리되는 동안 Cache 된 Pointer를 유지하면 Performance를 향상할 수 있다.
  • 단, ASC가 PlayerState에 있는 경우, PlayerState의 NetUpdateFrequency를 늘려야 한다.
  • Gameplay Ability System은 다수의 Actor가 하나의 Ability System Component를 공유하는 것은 지원한다.
    • 하지만 하나의 Actor가 다수의 Ability System Component를 가지는 것은 지원하지 않는다.
    • Actor의 Ability system Component에 변경사항을 적용하고 이를 얻을 때 모호함을 유발할 수 있기 때문.
 

Replicate Actor Properties In Unreal Engine | Unreal Engine 5.4 Documentation | Epic Developer Community

Learn how to replicate actor properties in Unreal Engine; including conditional replication, custom conditions, and object references.

dev.epicgames.com

ASC가 PlayerState에 있는 경우

  • 더보기
    // Server only
    void AGDHeroCharacter::PossessedBy(AController * NewController)
    {
    	Super::PossessedBy(NewController);
    
    	AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
    	if (PS)
    	{
    		// Set the ASC on the Server. Clients do this in OnRep_PlayerState()
    		AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
    
    		// AI won't have PlayerControllers so we can init again here just to be sure. No harm in initing twice for heroes that have PlayerControllers.
    		PS->GetAbilitySystemComponent()->InitAbilityActorInfo(PS, this);
    	}
    	
    	//...
    }

     

    // Client only
    void AGDHeroCharacter::OnRep_PlayerState()
    {
    	Super::OnRep_PlayerState();
    
    	AGDPlayerState* PS = GetPlayerState<AGDPlayerState>();
    	if (PS)
    	{
    		// Set the ASC for clients. Server does this in PossessedBy.
    		AbilitySystemComponent = Cast<UGDAbilitySystemComponent>(PS->GetAbilitySystemComponent());
    
    		// Init ASC Actor Info for clients. Server will init its ASC when it possesses a new Actor.
    		AbilitySystemComponent->InitAbilityActorInfo(PS, this);
    	}
    
    	// ...
    }
    •  ASC가 PlayerState에 있는 경우에는 명시적으로 Initialize 하는데 호출되는 함수가 달라진다.
      • Server: Pawn::PossessedBy()
      • Client: Pawn::Onrep_PlayerState()
    • 이 모든 과정은 Client에 PlayerState가 있다는 것을 상정하고 작업된다.
    • 만약 아래 로그가 발생하면, ASC가 Client에서 Initialize가 되지 않은 것이다.
      • LogAbilitySystem: Warning: Can't activate LocalOnly or LocalPredicted ability %s when not local!

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Gameplay Ability System 소개  (0) 2024.05.10

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/gameplay-ability-system-for-unreal-engine?application_version=5.3

 

언리얼 엔진의 게임플레이 어빌리티 시스템 | 언리얼 엔진 5.4 문서 | Epic Developer Community

게임플레이 어빌리티 시스템 개요

dev.epicgames.com

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/understanding-the-unreal-engine-gameplay-ability-system?application_version=5.3

 

언리얼 엔진 게임플레이 어빌리티 시스템을 살펴봅니다. | 언리얼 엔진 5.4 문서 | Epic Developer Commu

게임플레이 어빌리티 시스템 및 각 컴포넌트 클래스가 어빌리티에 기여하는 방법을 살펴봅니다.

dev.epicgames.com

Gameplay Ability란?

  • RPG나 MOBA의 Ability, Attribute 구축을 지원하는 고도로 유연한 Framework
  • 다양한 인게임 Ability를 설계, 구현하고 효과적으로 연결할 수 있도록 기능을 지원
    • Action의 결과로 다양한 Attribute를 조절하거나 Effect를 생성
    • Timer나 자원 비용을 구현하여 Action의 빈도 조절
    • Ability의 Level과 그에 따른 Effect 변경
    • Particle 및 Sound Effect를 활성화
    • 점프와 같은 단순한 Action 기능
  • Gameplay Ability System과 상호작용하는 Actor에는 Ability System Component가 있어야 한다.
    • Ability를 활성화
    • Attribute 저장
    • Effect Update
    • Actor간 상호작용 처리
  • Game Ability System을 생성하고 Ability System Component가 있는 Actor를 생성하면
    Ability를 생성하고, Actor가 Ability에 어떻게 반응하는지 결정할 수 있다.

Gameplay Ability

  • UGameplayAbility를 상속받은 Class나 BP들을 지칭
    • 특정 Ability에서 어떤 역할을 하는지 정의
    • 해당 Ability의 Replication과 Instancing Activity와 같은 요소가 어떻게 처리될지 정의

Ability Task

  • Gameplay Ability에서 호출하는 일련의 비동기 구성 요소
    • UAbilityTask Class를 상속 받음.
  • C++로 작성된다.
  • 작업이 끝날 때 cpp의 Delegate나 BP의 출력 실행 핀을 호출한다.

Gameplay Attribute

  • FGameplayAttribute 구조체에 저장되는 float 값
    • Game이나 Actor에 특정한 방식으로 영향을 미치는 값들
  • 캐릭터의 health, power, jump height 등의 수치들이 이에 해당한다.

Gameplay Effects

  • Gameplay Attribute를 변경하는 수단
    • 즉시 변경할 수도 있고, 일정 시간동안만 변경할 수도 있다.
    • 후자는 버프/디버프

Owner Tracking

  • Ability와 Effect는 "Owner"라는 개념을 유지해야 한다.
    • Effect가 계산을 수행할 때에는 Owner가 누구인지 알아야 Attribute를 사용할 수 있다.
  • 이 때 Owner는 Network Replication의 것과 다르다.

State Tracking

  • Ability는 다음과 같은 State 순환을 Tracking 할 수 있어야 한다.
    • Ability가 활성화 된 시점
    • Ability의 실행이 현재 진행중인 시점
    • Ability가 완전히 완료되고 더 이상 활성화되지 않은 시점

실행 조정하기

  • Ability는 실행 도중 특정 타이밍에 System과 다양한 Interaction을 할 수 있어야 한다.
    • Animation Montage 활성화
    • Character의 Movmenet를 일시적으로 제어
    • Visual Effect Trigger
    • Overlap 및 Collision Effect 수행
    • 일시적 혹은 영구적으로 Attribute 변경
    • 인게임 Resource 증감
    • 다른 Ability 활성화 허용 또는 차단
    • Ability 사용을 제한하기 위한 쿨다운 처리
    • 인게임 Evnet에 의한 중단
    • 진행 중인 다른 Ability 취소
    • Character의 주요 State 변경사항 적용
      • 새로운 MovementMode 활성화
    • 다른 Interaction 도중 입력에 반응
    • UI에 Ability의 인게임 상태 전달

시스템 구성

  • Edit -> Plugin에서 Gameplay Abilities를 검색해 활성화, Editor 재시작
  • [MyProjectName].Build.cs에 다음 항목 추가
    • GameplayAbilities
    • GameplayTags
    • GameplayTasks

Various Components of Gameplay Ability System

Owner Tracking

  • Gameplay Ability를 사용하기 위해서는 Ability System Component를 Actor에 Attach해야 한다.
  • Ability System Component는 다음 기능을 제공한다.
    • Actor에 Ability 추가/제거
    • Actor가 소유한 Ability가 무엇인지 지속적으로 Tracking, 활성화
    • Ability system의 Context에서 소유 Actor를 나타내는 주요 수단
    • 다양한 요소를 Tracking하기 위한 System과 소유 Actor에 직접 Access하기 위한 Interface 제공
      • Attribute
      • 진행중인 Effect
      • Gameplay Tag
      • Gameplay Effect
  • Multiplayer Game 환경에서 Ability System Component는 다음 기능또한 제공한다.
    • Client 정보 Replicate
    • Server에 Player Action 전달
    • Client가 Ability System Component의 State를 변경 할 권한이 있는지 확인
  • Ability System Component의 부모 Actor는 원격 활성화를 수행하기 위해 로컬에서 제어되는 Player가 소유해야 한다.

Ability 활성화 및 실행 처리

  • Gameplay Ability는 Ability의 모든 Event를 실행하는 것을 담당하는 BP Object이다.
    • Animatino 재생
    • Effect Trigger
    • Owner로부터 Attribute 가져오기
    • Visual Effect 표시

Ability 활성화 제어

  • Gameplay Ability를 활성화 할 경우 System은 Ability가 진행중인 것으로 인식
  • 이후 활성화 Event에 Attach 된 Code를 실행해 완료 함수를 호출
    • Ability의 실행이 완료됨을 알리기 전까지 각 함수와 Gameplay 작업을 거쳐 이동
  • 정리 작업을 추가로 징행해야 하는 경우 완료 시 Event에 Code를 추가로 Attach 할 수 있다.
  • Ability를 취소하여 실행을 중단할 수 있다.
  • Gameplay Ability는 Gameplay Tag를 사용하여 실행하는 것을 제한한다.
    • 모든 Ability는 활성화 될 때까지 2가지 Tag 목록을 가지고 있는다.
      • 소유 Actor에 추가하는 Tag 목록
      • 활성화를 차단하거나 Ability를 자동으로 취소하는 Tag 목록
  • 자체 Code로 Ability 실행을 수동을 취소/차단/허용 할 수 있다.
  • Gameplay Ability는 다음 4가지 방법을 통해 활성화 할 수 있다.
    • Gameplay Ability Handle
      • BP나 C++에서 Ability를 명시적으로 활성화
      • Ability가 부여될 때 Ability System Component에 의해 제공 됨
    • Gameplay Event
      • 일치하는  Ability Trigger로 모든 Ability를 실행
      • Input/Define Machanism을 추상화해야 하는 경우에 유연성 극대화를 위해서는 이 방식을 적극 권장 함
    • 일치하는 Tag와 함께 Gameplay Effect
      • 일치하는 Ability Trigger로 모든 Ability 실행
      • GamePlay Effect의 Trigger Ability를 끄려는 경우 선호 됨
      • 비활성화 된 Animation을 재생하고 다른 Game Action을 차단하는 Ability를 Trigger하는 Sleep Deburf
    • Input Code
      • Ability system Component에 의해 추가 됨
      • 호출 시 일치하는 모든 Ability Trigger
      • Gameplay Event와 유사

Ability 실행 제어

  • Gameplay Ability는 일반적으로 사용되는 다양한 사용 사례와 기능을 제공
    • Cooldown
    • Resource 비용 할당
    • Animation
    • 기타 일반적인 Unreal Engine System을 처리하는 Preprocessed Gameplay Ability Working Library
  • 표준 BP Function Node가 실행을 즉시 종료할 때 상태를 계속 Tracking 하며 도중에 다른 Event를 실행
  • 부모 Gameplay Ability가 적절하게 취소되고 정리되었는지 Tracking 가능
  • Game에서 Gameplay Ability 작업을 확장해 Custom Gameplay Logic 구현하는 것이 일반적인 용례이다.
  • 소유 Actor에서 Gameplay Tag와 Event Data Struct를 수신하기 위해 대기하는 Gameplay Event에 응답 가능.

Attribute Set 및 Attribute

  • Gameplay Attribute System은 Attribute로 구성된 Attribute Set를 통해 Actor와 Interact한다.
    • Attribute Set은 계산에 사용되는 소수값으로, 보통 핵심 통계값을 Tracking 하는데 사용된다.
  • 기본 변수 대비 Gameplay Attribute 사용을 했을 시 얻는 이점이 몇가지 있다.
    • System을 Build할 수 있는 일관적이고 재사용 가능한 Attribute Group을 제공
    • Reflection을 통해 Access 할 수 있어 BL Editor에서 간단한 계산과 Effect 생성 가능
    • Default/Current/Max Value를 별도로 Tracking해 일시적인 수정 및 지속적인 Effect를 쉽게 생성
    • 값을 Client에 Replicate해 UI 시각화에 안전함
  • Actor에서 Gameplay Ability를 사용하려면 Ability System Component에 Attribute Set을 추가해야 한다.
    • 그러면 Ability System Component는 Attribute Set에 할당된 Attribute에 Access할 수 있게 된다.

Gameplay Effect 처리

  • Gameplay Ability System은 Gameplay Effect를 사용해 Target Actor에 변경사항을 적용한다.
    • Damage 적용
    • 지속되는 Effect를 적용하는 One-shot Effect
      • 이 경우에 Gameplay Effect는 제거될 때까지 Target Actor에 자신을 Attach한다.
      • 만료되거나 정리되기 전까지로 제한된 수명을 미리 설정 할 수 있다.
      • 혹은 Target Actor의 Gameplay attribute에 적용된 변경사항을 실행 취소할 수도 있다.
  • Gameplay Effect는 Attribute에 기반하여 계산을 한다.
    • BP Editor에서 간단하게 계산을 직접 생성할 수도 있다.
    • 하지만 복잡한 로직을 가지고 있고,
      다수의 Attribute에 동시에 영향을 미치는 Custom Effect 계산은 C++로 작업하는 것이 적절하다.
  • 보통 Gameplay Ability의 Owner Actor와 Target Actor 모두에서 정보를 처리할 수 있다.
    • 그렇기에 일반적으로 계산을 재사용이 가능한 코드로 결합할 수 있다.

Cosmetic Effect 처리

  • Gameplay Queue는 Visual Effect와 Sound Effect 실행을 담당하는 Actor 및 UObject이다.
    • Multiplay Game에서는Cosmetic Feedback을 Replicate하는데 선호되는 Method이다.
    • 생성할 때 Event Graph 내에서 재생할 Effect를 위한 Logic을 실행
    • 일련의 Gameplay Tag와 연관될 수 있으며, 일치하는 Gameplay Effect를 자동으로 적용한다.
  • Gameplay Queue는 신뢰할 수 있는 Replication을 사용하지 않는다.
    • 때문에 일부 Client가 Queue를 수신하지 못하거나, Feedback을 표시하지 못하는 경우가 있다.
  • Gameplay Code와 결합되어 있는 경우 이 때문에 동기화가 해제될 수 있다.
    • 따라서 Gmaeplay Queue는 Cosmetic Effect에서만 사용되어야 한다.
  • 모든 Client가 Replicate 되어야 하는 경우에는 Replication 처리를 위해 Ability 작업에 의존해야 한다.
    • Montage 재생 Ability 작업이 이에 적합한 예시이다.

Network Multiplay

Ability System Component 및 Replication

  • Ability System Component가 모든 Component에 전체 State를 Replicate하면 안된다.
    • 대역폭 절약 및 부정행위 방지 목적
  • 모든 Client에 Ability와 Gamplay Effect를 Replicate하면 안된다.
    • 영향을 받는 Gameplay Attribute와 Tag만 Replicate 해야 한다.

Ability Replicate 및 Prediction 사용

  • Network Game의 Ability 대부분은 Server에서 실행되고 Client로 Replicate되어야 한다.
    • 이로 인해 Ability 활성화에 지연이 발생하는 것은 일반적인 현상이다.
  • 만약 빠른 속도로 진행되어야 하는 Multiplay Game의 경우 Ability를 Local로 활성화 하고,
    그 사실을 Server에 전달해 동기화 하는 방식을 사용한다.
  • Server가 Ability 활성화를 거부하는 경우 Local로 적용한 변경사항을 실행 취소해야 한다.
    • 일부 Gameplay Effect의 경우 Server에 의해 거부된 경우에 대한 롤백 기능을 지원한다.
    • 대부분의 즉각적이지 않은 Gameplay Effect가 포함된다.
    • Damage나 즉각적인 기타 Attribute/Tag 변경사항은 제외된다.

Server가 소유한 Object와 Interact 하기 위한 Ability 사용

  • Gameplay Ability는 Bot, NPC, Server 소유의 기타 Actor 및 Object와의 Interact를 처리할 수 있다.
    • 이는 Local 소유의 Actor(Player/Pawn), Replicated Ability, 비 GAS Server RPC를 통해 수행되어야 한다.
    • 위 호출은 Interaction을 Server에 Replicate한 다음 NPC와의 변경사항을 수행하는 권한을 가진다.

'UE5 > GAS' 카테고리의 다른 글

[GAS] Ability Task  (0) 2024.05.16
[GAS] Gameplay Effect  (0) 2024.05.15
[GAS] Attribute Set  (1) 2024.05.14
[GAS] Gameplay Attribute  (0) 2024.05.13
[GAS] Ability System Component  (0) 2024.05.13

+ Recent posts