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都沒有被列入。
- PlayMontageAndWait
- 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並完善他們。
留言
張貼留言