アニメーション
概要
Quantumにはアニメーションを扱うための2つの異なる方法があります:
- Unityからゲーム状態をポーリングする;
Quantum Animator Addon
を使用した決定論的アニメーション。
ポーリングベースのアニメーション
ほとんどのゲームは、アニメーションを使用してオブジェクトの状態をプレイヤーに伝えます。たとえば、プレイ可能なキャラクターが歩いたりジャンプしたりするとき、アニメーションは実際にはその場でのアニメーションであり、知覚される動きはコードによって駆動されます。
言い換えれば、キャラクターのそれぞれの(Unity)アニメーターを管理するスクリプトはステートレスで、ゲームシミュレーション(Quantum)からポーリングしたデータに基づいてアニメーションパラメータとして渡す値を単に導出します。
注: ゲームプレイシステムがRoot Motionに依存している場合やアニメーション状態を認識する必要がある場合は、次のセクションに進んでください。
以下は、関連するUnityコンポーネントをキャッシュし、更新ビューコールバックから取得したQuantumGameからフレームを取得し、フレームから関連データを読み込んでアニメーターに適用する基本的なコードスニペットです。Unityでは、フレームAPIを読み取り専用操作にのみ使用するようにしてください。Unityから書き込むことは非決定的になるためです。
C#
namespace Quantum {
using UnityEngine;
public class CharacterAnimations : QuantumCallbacks
{
private QuantumEntityView _entityView;
private Animator _animator;
private void Awake() {
_entityView = GetComponent<QuantumEntityView>();
_animator = GetComponentInChildren<Animator>();
}
public override void OnUpdateView(QuantumGame game) {
var frame = game.Frames.Predicted;
var kcc = frame.Get<CharacterController3D>(_entityView.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 f, ref PlayerMovementFilter filter)
{
var input = f.GetPlayerInput(filter.PlayerID->PlayerRef);
if (input->Jump.WasPressed)
{
f.Events.PlayerJump(filter.EntityRef);
filter.Kcc->Jump(f);
}
}
}
}
Unity側では、UnityコンポーネントがPlayerJump
イベントをリスンし、それに反応することができます。このために必要なステップは以下の通りです:
- イベントを受け取ることができるメソッドを定義します -
void Jump(EventPlayerJump e)
。 - 対象のイベントにサブスクライブします。
- イベントを受信した際、スクリプトが存在するGameObjectのためのものであるかどうかを確認するために、イベントに含まれる
EntityRef
を以前にキャッシュしたものと比較します。 - Unity Animatorでパラメータをトリガーするか、設定します。
イベントの定義は、任意の.qtn
ファイル内で行います。
C#
event PlayerJump { EntityRef EntityRef; }
Unityコンポーネントでイベントに応答します。
C#
namespace Quantum
{
using UnityEngine;
public class CharacterAnimations : QuantumCallbacks
{
private QuantumEntityView _entityView;
private Animator _animator;
private void Awake()
{
_entityView = GetComponent<QuantumEntityView>();
_animator = GetComponentInChildren<Animator>();
QuantumEvent.Subscribe<EventPlayerJump>(this, OnPlayerJump);
}
private void OnPlayerJump(EventPlayerJump e)
{
if (e.EntityRef == _entityView.EntityRef)
{
_animator.SetTrigger("Jump");
}
}
}
}
ヒント
- モデルとそのアニメーターコンポーネントを子オブジェクトに配置します。
- イベントはゲーム状態の一部ではないため、遅れて参加したり再参加したプレイヤーには利用できません。そのため、ゲームがすでに開始されている場合は、最新のゲーム状態をポーリングしてアニメーション状態を最初に初期化することをお勧めします。
- 100%の精度でトリガーされる必要があるアニメーションには、同期イベントを使用します。例えば、勝利の祝賀などです。
- 即時の応答が必要なアニメーションには、通常の非同期イベントを使用します。例えば、攻撃を受けたときなどです。
EventCanceled
コールバックを使用して、キャンセルされた非同期イベントによってトリガーされたアニメーションから優雅に退出します。これは、イベントが予測の一部として発生したが、検証されたフレーム中にロールバックされた場合に発生することがあります。
決定論的アニメーション
決定論的アニメーションシステムの主な利点は、ティック単位で正確なアニメーションであり、すべてのクライアント間で100%同期され、ロールバックが発生した場合には正しい状態にスナップすることです。理想的に聞こえるかもしれませんが、アニメーションとその状態がシミュレーションされたゲーム状態の一部となるため、パフォーマンスに影響を与えます。実際には、決定論的アニメーションシステムを必要とし、それから利益を得るゲームは少数です。これには、対戦格闘ゲームや一部のスポーツゲームが含まれます。
Quantum Animatorは、決定論的アニメーションを可能にするツールです。このツールは、UnityのMecanim Controllerから情報をベイクし、状態や状態間の遷移、モーションクリップなどのすべての構成をインポートすることで機能します。
Quantum 3以降、コードはオープンソースとなり、Addons > Animator
ページでダウンロード可能です。このページでは、ツールのインポートと使用方法に関する概要やクイックガイドも提供されています。
その機能は限られていることを念頭に置き、必要に応じて適応させる必要があるでしょう。
ヒント
- Quantum Animatorを使用する前に、アニメーションがゲームプレイに結びついているのか、それとも単なる視覚的表現なのかを検討してください。前者の場合、Quantum Animatorは適切な解決策ですが、そうでない場合はポーリングベースのアニメーションが適しています。