Animation
概述
在 Quantum 中,有兩種不同的方式來處理動畫:
- 從 Unity 輪詢遊戲狀態;及,
- 使用
Quantum Animator Addon
的確定性動畫。
基於輪詢的動畫
大多數遊戲使用動畫來向玩家傳達物件的狀態。例如,當可玩角色行走或跳躍時,動畫實際上是 原地 動畫,而感知到的移動是由代碼驅動的。
換句話說,管理角色(Unity)動畫控制器的腳本是無狀態的,僅根據從遊戲模擬(在 Quantum 中)輪詢的數據推導出值,並將其作為動畫參數傳遞。
__注意: __ 如果遊戲系統依賴 根運動 或需要知道動畫狀態,請跳至下一節。
以下是一個代碼片段,展示了緩存相關 Unity 組件的基本方法,並從更新視圖回調中獲取 QuantumGame 的一幀,然後從該幀中讀取相關數據並應用到動畫控制器上。在 Unity 中,請確保僅使用幀 API 進行 只準備好的 操作,因為從 Unity 寫入會導致非確定性行為。
C#
namespace Quantum
{
using UnityEngine;
public class CharacterAnimations : QuantumEntityViewComponent
{
private Animator _animator;
public override void OnInitialize()
{
_animator = GetComponentInChildren<Animator>();
}
public override void OnUpdateView()
{
var kcc = PredictedFrame.Get<CharacterController3D>(EntityRef);
_animator.SetFloat("Speed", kcc.Velocity.Magnitude.AsFloat);
}
}
}
觸發事件
某些動畫基於遊戲中發生的特定事件;例如,玩家按下跳躍鍵或被敵人擊中。在這些情況下,通常更傾向於從模擬中觸發一個事件,並讓視圖監聽它。這確保了解耦,並與基於輪詢的動畫方法配合良好。
有關事件和回調的全面解釋,請參考手冊中的 Quantum ECS > Events & Callbacks
頁面。
在 Quantum 中,系統可以在角色跳躍時觸發一個 Quantum 事件:
C#
namespace Quantum
{
using Photon.Deterministic;
public unsafe struct PlayerMovementFilter
{
public EntityRef EntityRef;
public PlayerID* PlayerID;
public Transform3D* Transform;
public CharacterController3D* Kcc;
}
unsafe class MovementSystem : SystemMainThreadFilter<PlayerMovementFilter>
{
public override void Update(Frame frame, ref PlayerMovementFilter filter)
{
var input = frame.GetPlayerInput(filter.PlayerID->PlayerRef);
if (input->Jump.WasPressed)
{
frame.Events.PlayerJump(filter.EntityRef);
filter.Kcc->Jump(f);
}
}
}
}
在 Unity 端,一個 Unity 組件可以監聽PlayerJump
事件並對其作出反應。以下是實現此功能的必要步驟:
- 定義一個可以接收事件的方法 -
void Jump(EventPlayerJump e)
。 - 訂閱相關事件。
- 當接收到事件時,通過比較事件中包含的
EntityRef
與先前緩存的,檢查事件是否針對腳本所在的 GameObject。 - 觸發 / 設置 Unity 動畫控制器中的參數。
事件的定義(在任何 .qtn 文件中):
Qtn
event PlayerJump { EntityRef EntityRef; }
在 Unity 組件中對事件作出反應:
C#
namespace Quantum
{
using UnityEngine;
public class CharacterAnimations : QuantumEntityViewComponent
{
private Animator _animator;
public override void OnInitialize()
{
_animator = GetComponentInChildren<Animator>();
QuantumEvent.Subscribe<EventPlayerJump>(this, OnPlayerJump);
}
private void OnPlayerJump(EventPlayerJump e)
{
if (e.EntityRef == EntityRef)
{
_animator.SetTrigger("Jump");
}
}
}
}
提示
- 將模型及其動畫控制器組件放在子物件上。
- 事件不是遊戲狀態的一部分,因此對於遲加入/重新加入的玩家不可用。因此,如果遊戲已經開始,建議首先通過輪詢最新的遊戲狀態來初始化動畫狀態。
- 對於需要 100% 準確觸發的動畫(例如勝利慶祝),使用同步事件。
- 對於需要快速響應的動畫(例如被擊中),使用常規的非同步事件。
- 使用
EventCanceled
回調來優雅地退出由取消的非同步事件觸發的動畫。這可能發生在事件作為預測的一部分觸發,但在驗證幀期間被回滾時。
確定性動畫
使用確定性動畫系統的主要優勢是幀精確的動畫,這些動畫在所有客戶端之間 100% 同步,並且在回滾時會立即恢復到正確的狀態。雖然這聽起來很理想,但它會帶來性能影響,因為動畫及其狀態現在是模擬遊戲狀態的一部分。實際上,只有少數遊戲需要並受益於確定性動畫系統;其中包括格鬥遊戲和 某些 體育遊戲。
Quantum Animator 是一個實現確定性動畫的工具。它通過從 Unity 的 Mecanim 控制器烘焙信息並導入每個配置(例如狀態、狀態之間的轉換、運動剪輯等)來工作。
自 Quantum 3 起,代碼已開源,並可在Addons > Animator
頁面下載。該頁面還提供了工具的概述和快速指南,說明如何導入和使用它。
請記住,它的功能有限,可能需要根據您的需求進行調整。
提示
- 在使用 Quantum Animator 之前,請考慮動畫是否與遊戲玩法綁定,或者僅僅是其視覺表現。如果是前者,Quantum Animator 是一個合適的解決方案;否則,基於輪詢的動畫是更好的選擇。