UE4 GameplayAbilityTask介紹


簡介

本篇介紹的是UE4引擎的Gameplay Ability System(GAS)中的AbilityTask系列。
不知道什麼是GAS的話可以參考先前的文章

GameplayAbilities System介紹 (一)

GameplayAbilities System介紹 (二)

UE4 GameplayAbilitySystem - GameplayEffect & GameplayCue 如何設定參數


Gameplay AbilityTask介紹


AbilityTask建立了大量的範例,作為示範,很多ASC內部的系統與其他系統的溝通都做在AbilityTask內。
而AbilityTask則是要在Gameplay Ability內串流程的時候使用,AbilityTask節點不會出現在一般的Blueprint(BP)內

所以當了解完如何使用ASC,如何創造一個GameplayAbility,以及GameplayEffect後。
應該先試著了解GameplayTask,可以對ASC能夠做到的事情,如何做事情有更深刻的了解。


以下是引擎提供的Tasks,這些都可以在
Engine\Plugins\Runtime\GameplayAbilities\Source\GameplayAbilities\Private\Abilities\Tasks\
裡面找到

  • RootMotion
    • ApplyRootMotion_Base
    • ApplyRootMotionConstantForce
    • ApplyRootMotionJumpForce
    • ApplyRootMotionMoveToActorForce
    • ApplyRootMotionMoveToForce
    • ApplyRootMotionRadialForce
  • MoveToLocation
  • NetworkSyncPoint
  • PlayMontageAndWait
  • Repeat
  • SpawnActor
  • StartAbilityState
  • VisualizeTargeting
  • Wait
    • WaitAbilityActivate
    • WaitAbilityCommit
    • WaitDelay
    • WaitGameplayEvent
    • WaitGameplayTag
    • WaitGameplayTagBase
    • WaitMovementModeChange
    • WaitOverlap
    • WaitTargetData
    • WaitVelocityChange
  • WaitAttribute系列
    • WaitAttributeChange
    • WaitAttributeChangeRatioThreshold
    • WaitAttributeChangeThreshold
  • WaitInput系列
    • WaitCancel
    • WaitConfirm
    • WaitConfirmCancel
    • WaitInputPress
    • WaitInputRelease
  • WaitGameplayEffect系列
    • WaitGameplayEffectApplied
    • WaitGameplayEffectApplied_Self
    • WaitGameplayEffectApplied_Target
    • WaitGameplayEffectBlockedImmunity
    • WaitGameplayEffectRemoved
    • WaitGameplayEffectStackChange

各細項說明

RootMotion

示範Ability如何透過RootMotionSource跟Character的RootMotion系統連動。
經過實測PlayAnimMontageAndWait以及MoveToForce在lag 200ms是有拉回現象的,但是JumpForce沒有。

在UE4.25似乎有更新,有對這部份的程式碼做修改,不過我還沒有機會做測試。


MoveToLocation

會改SetMovementMode為Custom,無視碰撞。
感覺是個示範用的Task,想不到實際用途。

NetworkSyncPoint


用來做server/client同步的節點,總共有BothWait,OnlyServer,OnlyClient三種。
訊息處理的來回都是使用RPC。

Client 的部分會多加上prediction window,可以用來作client prediction。
例如client先上effect或是gameplay cue,server再apply effect。

如果要做上面說的Client prediction只有一種接法:OnlyServerWait,OnSync後面接server與client都要apply effect的程式碼(或是BP node)。如下圖



這樣接的話,執行順序會是這樣:
Client透過Input先發動Ability
Ability內執行WaitNetSync
因為Client不用等待,所以直接ApplyEffect (Client Prediction)
同時間Client也會送出RPC,並且把prediction key傳過去
Server也會知道Ability發動,然後執行WaitNetSync,等Client的RPC過來。
當收到Client的RPC過來後,Server才會通過OnSync,然後ApplyEffect
ApplyEffect後,Server也會給一個回應,讓Client知道Server已套用這個Effect。

如果使用BothWait是不行的,因為這樣Client端OnSync後是等到Server執行完OnSync才會執行,這時才上Effect不僅不是Prediction,Client還會多上一次Effect,然後再被回朔(因為Server已套用過)。

BothWait應該是要拿來處理其他事情的,不過我目前沒想到使用情境。

PlayMontageAndWait

應該是最常用的節點,讓角色做montage,並且會負責處理server/client的同步,
嚴格來說是處理Authroity與SimulatedProxy的同步,Autonomous要自行決定時機呼叫PlayMontageAndWait

Repeat

就是一個timer的loop,每隔一段時間做一次,做到設定的次數就結束。

SpawnActor

從TargetDataHandle內取出第一個TargetData。
從TargetData拿HitResult,或是EndPoint作為Spawn的座標。
如果都沒有,Spawn座標會是執行這個Task的OwnerActor所在位置。

StartAbilityState

讓Ability進入自定義的AbilityState。
有三種方式會進入OnStateEnded
1. Ability結束
2. 手動呼叫AGameplayAbility::EndAbilityState
3. 另一個StartAbilityState啟動並且勾選bEndCurrentState

AGameplayAbility::CancelAbility被呼叫的時候會進OnStateInterrupted。

VisualizeTargeting

Spawn target actor,並且呼叫TargetActor->StartTargeting。
在StartTargeting裡面會讓TargetActor知道可以Visualize TargetActor。
TargetActor會把視覺化的部份交給AGameplayAbilityWorldReticle做處理。
因為我的實驗環境沒有實作TargetActor相關的函式,所以實際上我沒有測試效果是怎麼樣。

Wait系列

WaitAbilityActivate

偵測AbilityActivate的事件,並且取得Ability Reference
透過GameplayTag/Query/TagRequirements來決定事件的通知。

WaitAbilityCommit

偵測AbilityCommit的事件,並且取得Ability Reference

WaitDelay

Delay一段時間

WaitGameplayEvent

非常重要的Task,GAS系統與外部系統大多都是使用Event在溝通。
通常用法是等待多個事件,再根據不同的Tag決定事件要如何處理。

WaitGameplayTag

分為Add或是Remove。
核心是靠AbilitySystemComponent(ASC)->RegisterGameplayTagEvent

RegisterGameplayTagEvent實際上對GameplayTagEventMap註冊事件
GameplayTagEventMap事件只有TagCount變為0或是從0變為其他值才會發送

如果想知道TagCount變動事件要在註冊的時候Type設為EGameplayTagEventType::AnyCountChange

搜尋RegisterGameplayTagEvent可以看到範例。

WaitMovementModeChange

跟Chatacter->MovementModeChangedDelegate註冊事件

呼叫SetWaitingOnAvatar,跟Ability說有個Task註冊跟Avatar相關的事件。如果AvatarActor有問題會直接結束執行這個Task的Ability。

WaitOverlap

對Avatar的PrimitiveComponent註冊OnComponentHit事件

事件發生後製作TargetData,並轉成Handle送出。

可能只能當作範例,直接使用不太好用。
1. 通常會知道要關注哪個Component。
2. 通常不會每個Hit都需要直接轉成Handle,有可能還要做一些過濾。

WaitTargetData

負責處理一整套作SpawnTargetActor->ConfirmTarget->產生TargetData的流程,也會負責處理網路同步(Client給Server)

註解有說明每次都會SpawnActor,所以效率不是很好,也提到這個Task並沒有經過大量驗證,但是可以用來學習Target同步的方法。

只要不是CustomMulti的ConfirmationType,都會直接EndTask,並且殺掉SpawnedTargetActor。 (OnDestroy)

也就是說,當Confirm後,TargetActor就刪除了。除非是CustomMulti。

WaitVelocityChange

利用Tick檢查ActorInfo的速度是否大於參數Magnitude,如果有就發送事件。

WaitAttribute系列

OptionalExternalOwner讓這個系列的Task可以偵測特定的角色,不侷限於Task的擁有者。

WaitAttributeChange

偵測AttributeChange有變動的時候發送事件。
可以使用SrcTag來過濾

WaitAttributeChangeRatioThreshold

偵測兩個Attribute的比例改變的時候發送事件。由ComparsionType決定什麼條件才會發送事件

WaitAttributeChangeThreshold

偵測Attribute的比例改變的時候發送事件。由ComparsionType決定什麼條件才會發送事件。

WaitInput系列

WaitCancel

偵測GenericLocalCancelCallbacks事件,玩家觸發InputAction:AbilityCancel的時候會收到。

WaitConfirm

LocalPredict的Ability如果想知道Server已Confirm這個Ability的話,透過這個事件可以知道。

Client::ActivateAbility->Client::WaitConfirm進入等待
Server::ActivateAbility->Client收到AbilityConfirm事件發送,Client才會通過WaitConfirm。

WaitConfirmCancel

跟WaitConfirm名字很像但意義差很大,這邊指的是玩家已經進入Targeting,結果在等待Confirm的階段取消的事件。

以實際上案例說明就是,玩家按了某個鍵會舉槍(進入Targeting),要再按另一個鍵A才會開槍(Confirm)。或是按另一個鍵B把槍放下(ConfirmCancel)

按下A就是進WaitConfirm。
按下B就是進WaitConfirmCancel。

WaitInputPress

AbilitySystemComponent::AbilityLocalInputPressed 裡面做檢查
AbilitySystemComponent::BindAbilityActivationToInputComponent 裡面定義按鍵
按鍵定義都在FGameplayAbilityInputBinds
GiveAbility的時候要定義Ability與inputID的關係

WaitInputRelease

AbilitySystemComponent::AbilityLocalInputReleased
其餘關鍵與InputPress一樣。

WaitGameplayEffect系列

WaitGameplayEffectApplied_Self與Target


Effect被Apply的時候會收到事件。
條件很多,除了可以用Source/Target的tag當過濾條件,還可以用Query當過濾條件,是不是要收到Periodic Effect事件也可以分開決定。

SourceFilter是比較少看到的過濾條件,預設可以過濾掉不是自己,或只能是自己,或是特定的actor等等的條件,也可以製作自定義的Filter。

WaitGameplayEffectBlockedImmunity

如果有Effect被Immunity的功能擋掉的話會收到通知。

WaitGameplayEffectRemoved

Effect被移除的時候送通知,需要傳入EffectHandle。

WaitGameplayEffectStackChange

Effect堆疊數量有變動的時候送通知,需要傳入EffectHandle。


常用的GameplayTasks與使用情境


這邊列出我們專案常用的Task組合,需要注意的是我們專案沒有引入input與target,所以這兩個部分相關的Task都沒有被列入。

  1. PlayMontageAndWait
  2. WaitGameplayEvent

在我們的專案大量使用到Ability觸發->播放動作->動作的某個時機發射子彈->傷害判定與計算,這樣的模式。
所以PlayMontageAndWait是我們的技能一定會呼叫的,這部份算是簡單易懂。

不過動作的時機要如何回到Ability,就需要靠WaitGameplayEvent了。

首先要做一個AnimNotify,開出GameplayTag為Editable參數,然後將Tag作為參數呼叫SendGameplayEventToActor,這樣發事件就結束了。如下圖
Example of send gameplay event in animation notify.


回到Ability的BP,我們需要的是註冊GameplayEvent,所以利用WaitGameplayEvent作事件等待。
如此一來,只要Animation notify填的gameplayTag與Ability等待事件的Tag有對上,就能夠在這個Ability得到事件的通知。

在ActionRPG的範例裡面,他們直接延伸PlayMontageAndWait,整合一事件通知與動作撥放,名為PlayMontageAndWaitForEvent。確實是很方便。如下圖的Event Tags。



但是Ability並不一定只想收到動作的事件通知,有可能會從其他系統的事件過來,如此一來還是需要撰寫WaitGameplayEvent。最後我們為了有統一的入口,並沒有採取PlayMontageAndWaitForEvent的作法。


我們擴充的是ActionRPG中EffectContainerMap的概念,對EffectContainerMap內將所有的Tag註冊事件,然後統一在HandleGameplayEvent裡面處理。這樣不管是外部事件,動畫事件,幾乎只需要看Ability的EffectContainerMap欄位就能了解這個Ability的行為與效果。如下圖:


假設一個ContainerMap裡面有兩筆資料
一個EventTag 是SpawnBullet 然後Effect是給傷害
一個EventTag 是ApplyBuff 然後Effect是回血

這樣企劃或程式只要打開這個ability看ContainerMap的部份,就可以知道
"嗯...這應該是會發射一個子彈給對方傷害,然後打中可能會回血,或是發射就回血的技能"

如果想要知道回血時機,就是透過Tag搜尋功能去查ApplyBuff是在哪裡被用到就可以了。

結論

引擎雖然提供很多AbilityTask,但是我稍微看過一輪之後覺得AbilityTask比較像是一個教學範本。學習者應該從裡面的程式碼知道AbilitySystem系統內如何溝通,以及如何跟外部系統溝通,以及如何處理網路同步。

直接就使用內建的可能會遇到很多問題,但是相信他們都把複雜的情況做出來了,剩下的就是靠使用者繼承這些Task並完善他們。






留言

這個網誌中的熱門文章

UE4 除錯技巧分享 (一)

UE4 Navigation Mesh 心得