Custom Animator
イントロダクション
Quantumの決定性アニメータはUnityのMecanim Controllerの情報をベイクすることで作用します。ステート、移行、モーションクリップなどの全ての設定をインポートします。
これを使用する主な利点は、マシン間で100%同期するようにアニメーションを制御できることです。アニメーションは正しい状態にスナップし、予測ロールバックに従って時間をクリップします。そのため、非常に正確なティックベースのアニメーションを提供することができます。これは、すべてのクライアントシミュレーション間でアニメーションが完全に同期している必要があるため、格闘ゲームや一部のスポーツゲームで通常必要とされるものです。
このアドオンがUnityのMecanimに依存していることと、Unityに新しいアニメーションツールがないことから、アドオンの改善を中止することにしました。このツールを使用する場合、あなたのゲームのアニメーションの必要性に基づいて、チームがコードを変更する必要があるかもしれません。
そのため、アニメーターをオープンソースとして公開し、quantum.codeプロジェクトにインポートして変更できるようにしました。
このドキュメントではカスタムアニメータのインポート方法とプロジェクト内での使用方法について説明します。
こちらのパッケージはQuantum SDK 2.1.0 A1の夜間ビルド384で最後にテストを行いました。
既知の問題/制限
デフォルトのCustomAnimatorには、以下の既知の制限があります:
- 階層的な状態に対応していません
- Unityのアニメーションイベントに対応していません
- モーションのないステート(アニメーションクリップが設定されていない)に対応していません
- レイヤー(Layers) メニューに対応していません。つまり、アバターマスク(Avatar Masks)やブレンド(Blending)などを使用して、複数のレイヤー間でアニメーションを混ぜることができません
- ブレンドツリー(Blend Trees)を使用する場合、1Dと2Dの両方のブレンドツリー(Blend Trees)で、ブレンド値を[-1, 1]の間に設定する必要があります;
- 3D Transforms/PhysicsBodyのRoot Motionに対応していません。
カスタムアニメータパッケージをインポートする
- ここからカスタムアニメータをダウンロードします。;
- カスタムアニメータを解凍し、
QuantumCustomAnimator_state
フォルダとQuantumCustomAnimator_systems
フォルダのコンテンツをquantum.code
プロジェクトの任意の場所に入れます。; - このパッケージには
Frame.InitUser
メソッドの部分実装が搭載されています。このメソッドはCustomAnimatorUpdater
の初期化を処理します。既にプロジェクトに同メソッドの部分実装がある場合はInitUser
内で忘れずにInitializeAnimatorUpdater
メソッドへの呼び出しを含めるようにしてください。そして、このパッケージからFrame.User.cs
ファイルをインポートしないでください。; - 自分のソリューションに全てそろったら、
quantum.systems
ソリューションをビルドします。; - Unityで、プロジェクトに
QuantumCustomAnimator.unitypackage
ファイルをインポートします。;
これでビルドと実行の初めのステップが完了しました。それでは、コンポーネント、アセット、ベイキングツールを実際に使用していくにあたっての説明を以下に記します。
カスタムアニメータを使用する
- このコンポーネントを使用するには、Unity 上の Entity Prototype に直接追加します。また、
CustomAnimator
コンポーネントの新しいインスタンスを作成し、コードで直接エンティティに追加することもできます:
C#
var customAnimator = new CustomAnimator();
f.Set(fighter, customAnimator);
- Unity上で、
CustomAnimatorGraph
型の新しいデータアセットを作成します。これは Unity の Mecanim コントローラーに関連する情報を保存するアセットです。Entity Prototypes を使用してカスタムアニメーターを設定する場合、Inspector のAnimatorGraph
フィールドで Animator Ggraph アセットを参照することができます。別の方法として、コードでグラフアセットを読み込むこともできます:
var animatorGraphAsset = f.FindAsset<CustomAnimatorGraph>(assetGuid);
- CustomAnimatorコンポーネントの初期化:エンティティプロトタイプを使用し、上記のようにCustomAnimatorGraphを定義する場合、コンポーネントを初期化する必要はありません。しかし、コードで初期化することもできます:
C#
CustomAnimator.SetCustomAnimatorGraph(&knight->CustomAnimator, animatorGraphAsset);
f.Set(fighter, customAnimator);
- Unityでのやり方と同様に、Animatorへの読み書きにはGettersとSettersを使います:
C#
// Getters
CustomAnimator.GetBoolean(&fighter->Animator, "Defending");
CustomAnimator.GetBoolean(&fighter->Animator, "Direction");
CustomAnimator.GetBoolean(&fighter->Animator, "Speed");
// Setters
CustomAnimator.SetBoolean(&fighter->Animator, "Defending", true);
CustomAnimator.SetInteger(&fighter->Animator, "Direction", 25);
CustomAnimator.SetFixedPoint(&fighter->Animator, "Speed", FP._1);
重要な留意点として、ここでは Unityのトリガーパラメータ がブーリアンのように作用します。つまり、パラメータをトリガーするのに、
SetBoolean(&fighter→Animator, "MyTrigger", true)
を使用してtrueに設定し、次のティックでSetBoolean(&fighter→Animator, "MyTrigger", false)
を設定しトリガーが再度有効化しないようにする必要があります。各トリガーパラメータを毎ティックごとにfalseに設定するプレアニメーションシステムもあります。;各
AnimatorState
にはカスタムアセットにリファレンスを作成できるAssetRef
があります。Unityのアニメータから情報を多く引っ張り、Unityのベイキングプロセスを拡張することも、これらの情報をカスタムアセットに保存してステート上で参照することも可能なので、シミュレーションで読み取ることができます。例えば、FPAnimationCurves
の形式でベイクされたhitbox・hurtbox情報をもつアセットを作成できます。Unityイベントの名前・時間をアセットに保存できるため、シュミレーション上でアニメーションイベントがいつ発生するかなど把握することができます。;SystemSetup.csファイル上で、自分のシステムの後に以下の行を挿入します。:
C#
new CustomAnimatorSystem(),
- Unityで、コンテキストメニュー
Create/Quantum/CustomAnimatorGraph
からカスタムアニメータグラフアセットを作成します。
デフォルトステートでは以下の様な構成になっています。:
Controller
フィールドを使用してUnityのAnimator Controllerアセットを参照し、Import Mecanim Controller
ボタンをたたきます。それから、ステート、移動、パラメータなどのアニメーション情報フィールドを確認してください。:
EntityView
クラスを開き、以下のフィールドを追加します。:C#
public CustomQuantumAnimator CustomQuantumAnimator;
アニメーション化するGameObjectsに
CustomQuantumAnimator
コンポーネントを追加します。もちろん、EntityPrefabRoot
は必要なので追加したコンポーネントへの参照を、上記の説明で作成したフィールドを使用して作成してください。;
- EntitiyViewUpdaterクラスを開きます。OnUpdateViewメソッド上にアクティブビュー間を行き来するforeachループがあります。これにはカスタムアニメータアップデートが含まれているはずですので、
foreach
は以下の様な構造になっています。:
C#
// Iterate over all view instances and update components using only entities from current frame.
foreach (var kvp in _activeViews) {
// ... updates 2D and 3D transforms
// At the end of the foreach, add this.
// This is responsible for actually animating the entity views
if (instance.CustomQuantumAnimator)
{
if (game.Frames.Predicted.Unsafe.TryGetPointer<CustomAnimator>(instance.EntityRef, out var animator))
{
instance.CustomQuantumAnimator.Animate(game.Frames.Current, animator);
}
}
}
- 最後に、UnityのAnimatorコンポーネントを自分のオブジェクトに追加します。
EntityPrefabRoot
のある同じオブジェクトに配置されるか、子オブジェクトに配置される可能性もあります。:
quantumコードでの置換が完了したので、今度はUnityサイドで置換を行います。Unityのmecanim controllerから直接ベイクした、情報を持つCustomAnimatorGraphもあります。準備が整いました。
コードの一部には簡単なコメントが付けられています。Quantumサイドには、AnimatorConditionsなど内部ステートマシンそのものに関連するものすべてがあります。AnimatorConditionsはAnimatorTransitions、AnimatorVariables (パラメータ)、Layers、Motionsなどで使用されています。
Unityサイドでは、Custom Animator Graphアセットのカスタムエディタの描写に関連するコードとベイキング手順の処理を行うすべてのコード(これは今後より多くのデータを衝突情報のFPAnimationCurves
、Unityのアニメーションイベントなどのアニメータアセットにベイクするために確実に拡張することになります)があります。