本文將介紹 Unity 6.3 新增的 Animator.ResetControllerState。
本文將 ResetControllerState 定位為「用來明確且簡單地實作歸還至物件池時的初始化的 API」,並根據在 Unity 6000.3.21f1 上確認的行為,說明安全的物件池歸還處理。
ObjectPool 是透過重複使用已建立的實例,來減少頻繁建立與銷毀負載的常見手法。Unity 的 ObjectPool 也具備可在取得、歸還、銷毀的時機插入處理的機制*1。
若為了放入物件池而將 GameObject 設為非啟用,再次啟用時 Animator 會執行 Rebind,可能抵銷物件池帶來的效能。因此,Unity 官方的 Animator 類別說明中建議停用元件,而非停用 GameObject*2。本文依循此建議,以下列物件池為前提:GameObject 保持啟用狀態存放,歸還時停用 Animator 與 Renderer,取得時再重新啟用。
另一方面,既然物件池會重複使用實例,GameObject 與元件就可能帶著上次使用時的狀態回來。Animator 也一樣,停用後停止的只有評估,內部狀態仍會保留。若什麼都不做,下次使用時會沿用上次使用時的 State 與各種 Parameter,因此可能從上次中途的 State 開始播放,或因殘留的 Parameter 而發生非預期的轉換。
ResetControllerState 是可將 AnimatorController 各 Layer 的 State 與 Parameter 一併還原為預設狀態的 API。在發行說明中,也將其介紹為讓動畫角色的物件池化更容易的新增功能*3。
若將 ResetControllerState 的引數 resetParameters 指定為 true,除了 State 之外,Parameter 也會還原為預設值。指定為 false 時,只會還原 State*4。將附有 Animator 的角色歸還至物件池時,通常會使用 ResetControllerState(true),將 State 與 Parameter 一併還原。無論用途是未使用、一般轉換、AnyState 轉換或 BlendTree,都會將 Float、Bool、Int 還原為預設值,並重設 Trigger。此外,也會將所有 Layer 的目前 State 還原為預設 State。
不過,反映的時機會因項目而異。Parameter 的值在呼叫後立即變為預設值,但 GetCurrentAnimatorStateInfo 在 Animator 下次被評估之前,仍會傳回重設前的 State。
AnimatorControllerPlayable 也提供了可重設 Controller 的 State 與 Parameter 的 ResetControllerState 方法*5。
另一方面,下列項目僅靠 ResetControllerState 不會還原為預設值。
Layer Weight 與 Animator.speed 是與 Controller 的 State、Parameter 不同的屬性。若不希望在歸還至物件池時保留變更後的值,請明確地將其還原。StateMachineBehaviour 的自訂變數也需要個別初始化。
此外,Rigidbody、ParticleSystem、AudioSource 等其他元件的狀態,以及自訂邏輯的狀態,是在 AnimatorController 之外管理,因此不在 ResetControllerState 的處理範圍內。
清單 2.1 是將歸還至物件池時的初始化與停用整理在一起的範例。預期從 ObjectPool<T> 的 actionOnRelease 呼叫。依專案的規範不同,其中也包含不需要的處理。
清單 2.1: 歸還至物件池時的 Animator 初始化範例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // 整理歸還至物件池時所需的初始化與停用 public void ReturnToPool() { // 還原 AnimatorController 各 Layer 的 State 與 Parameter animator.ResetControllerState(true); // defaultLayerWeight 需另外設定為重複使用時要還原的值 animator.SetLayerWeight(layerIndex, defaultLayerWeight); // 還原播放速度 animator.speed = 1f; // 僅在需要時使用。在歸還處理中完成重設後的第一次評估 animator.Update(0f); // 還原 Animator 之外的自訂狀態 ResetGameState(); // GameObject 保持啟用,停止評估與繪製後存放 animator.enabled = false; characterRenderer.enabled = false; } |
ObjectPool 並非只靠減少建立與銷毀的次數就算完成。必須決定重複使用的 GameObject 在下次使用時應從何種狀態開始,並將還原至該狀態的處理一併納入設計。
若不將 GameObject 設為非啟用,而是停用 Animator 後存放,Animator 的內部狀態會原樣保留。使用 ResetControllerState(true),可將 AnimatorController 各 Layer 的 State,以及 Float、Bool、Int、Trigger 的 Parameter 一併還原為預設值。另一方面,Layer Weight、Animator.speed、StateMachineBehaviour 的自訂變數,以及其他元件或自訂邏輯的狀態不會被重設。
將初始化的範圍區分為 AnimatorController 內與外,物件池歸還處理的職責就會更明確。建議以 ResetControllerState 為核心,只補上專案特有的狀態來組成歸還處理。