AnimationTree(アニメーションツリー)

はじめに

AnimationPlayerと合わせて、Godotはあらゆるゲームエンジンの中でもトップクラスの柔軟性をもつアニメーション・システムをもっています。トランスフォーム、ベジェ曲線、関数呼び出し、オーディオや副アニメーションのトラックが用意されているのみならず、あらゆるノードやリソースのどんなプロパティでもアニメーションできることは、非常に独特です。

ただし、それらのアニメーションをブレンドするには、AnimationPlayerでは固定のクロスフェード遷移のみなので、比較的制限されています。

AnimationTreeは、高度な遷移をするためにGodot 3.1で導入されたノードです。これは古いAnimationTreePlayerを置き換えるもので、膨大な量の機能と柔軟性を加えています。

AnimationTreeの作成

始める前にまず明らかにすべきは、AnimationTreeノードは自身のアニメーションは含みません。代わりにそれは、別にあるAnimationPlayerノードが持つアニメーションを利用します。これにより、従来通りアニメーションを編集(あるいは他の3Dシーンからインポート)でき、さらに再生をコントロールしたいときにこのノードを追加するのです。

AnimationTreeの最も一般的な使い道は3Dシーンです。シーンを3D交換フォーマットからインポートすると、通常はアニメーションはそのまま同梱されています(複数のものや、大きなものがインポートの際に分割されていても)。インポートされたGodotシーンでは最終的に、アニメーションはすべて一つのAnimationPlayerノードに格納されます。

Godotでは、インポートされたシーンを (インスタンス化あるいは継承せずに) 直接使うことは珍しいので、インポートされたシーンのための新しいシーンにAnimationTreeを追加することにします。そのあとAnimationTreeノードにて、インポートされたシーンの中に作られたAnimationPlayerを割り当ててください。

参考例として、三人称視点シューターデモ(英語) もそうなっています。

../../_images/animtree1.png

プレイヤー用の新しいシーンはKinematicBodyをルートにして作られています。このシーンの中には、元の.dae(Collada)ファイルがインスタンス化され、AnimationTreeノードが作成されてあります。

ツリーの作成

AnimationTreeでは主に3種類のノードが使えます:

  1. アニメーション・ノード。リンクされたAnimationTreeのアニメーションを参照します。
  2. アニメーションルート・ノード。副ノードとのブレンドに使われます。
  3. アニメーションブレンド・ノード。AnimationNodeBlendTreeの中で使用され、複数のインプットポートを持ち、一つのグラフでブレンドします。

AnimationTreeのルートノードを設定するには、いくつかの型が用意されています:

../../_images/animtree2.png
  • AnimationNodeAnimation: リストから選択したアニメーションを再生します。これは一番シンプルなルートノードで、通常は直接のルートとしては使用されません。
  • AnimationNodeBlendTree: mix、blend2、blend3、one shotなどのような、多くのブレンド型のノードを含んでいます。これはルートとしてよく使われるものの一つです。
  • AnimationNodeStateMachine: 一つのグラフに、複数のルートノードを子に持ちます。それぞれのノードはステートとして扱われ、ステートを切り替えるための機能をいくつも備えています。
  • AnimationNodeBlendSpace2D: ルートノードを2Dブレンドスペースに置けるようになります。二次元空間でブレンド位置をコントロールし、複数のアニメーションのミックスします。
  • AnimationNodeBlendSpace1D: 上記の簡略版です (一次元)。

ブレンド・ツリー

AnimationNodeBlendTreeはブレンド用で、ルートまたは通常のノードを含むことができます。ノードはメニューからグラフに追加します。

../../_images/animtree3.png

すべてのブレンドツリーには、標準でOutputノードが入っていて、アニメーションを再生するにはそこに何かをつなげる必要があります。

この機能を確認する一番簡単な方法は、Animationノードを直接つなげることです:

../../_images/animtree4.png

これは単純にアニメーションを再生します。実行する前にまずAnimationTreeがアクティブであることを確認してください。

下記は利用可能なノードの短い説明です:

Blend2 / Blend3

これらのノードは、2つあるいは3つのインプットをユーザー指定の値でブレンドします:

../../_images/animtree5.gif

より複雑なブレンドには、代わりにブレンドスペースを使うことをおすすめします。

ブレンドにはフィルターを使うこともでき、どのトラックをブレンド関数に渡すかどうかをコントロールできます。これはアニメーションをレイヤーに分けて重ね合わせるのに有用です。

../../_images/animtree6.png

OneShot

このノードは、副アニメーションを実行し、完了すると元に戻ります。フィルターだけでなく、フェードインとフェードアウトのブレンド時間もカスタマイズできます。

../../_images/animtree6b.gif

Seek

このノードは、グラフのどんな副子ノードに対してもシーク命令を送ります。時間をセットした後は、値は -1 に戻ります。

TimeScale

アニメーションの再生速度の変更 (あるいは逆再生) を可能にします。0 にセットするとアニメーションを一時停止します。

トランジション

とても単純なステートマシンです (StateMachineノードを扱いたくない時に)。複数のアニメーションを接続でき、トランジションの時間を指定できます。

BlendSpace2D

BlendSpace2Dは二次元でより高度なブレンドをするためのノードです。二次元空間に点を追加し、それらの位置をコントロールしてブレンドを決めます。

../../_images/animtree7.gif

X および Y の範囲でコントロールできます (それぞれ別の名称に変更可)。デフォルトでは、点はどこにでも置けます(座標系の上で右クリックするか、点を作成ボタンを使う)。三角形はドロネー分割で自動生成されます。

../../_images/animtree8.gif

三角形の自動作成をオフにすれば、三角形を手動で作成することもできますが、必要になることはあまり無いでしょう:

../../_images/animtree9.png

最後に、ブレンドモードは変更することができます。標準状態では、一番近い三角形の中で補完された点によってブレンドされますが、もし対象が(フレームごとに動かす)2Dアニメーションなら、離散モードに切り替えてもよいでしょう。あるいは、離散アニメーション間においても現在の再生位置を維持したい場合のために、キャリーモードもあります。モードはブレンドメニューから変更できます。

../../_images/animtree10.png

BlendSpace1D

これは2Dブレンドスペースと同様ですが、一次元のみです (そのため三角形は不要)。

StateMachine

このノードは比較的シンプルなステートマシンです。ルートノードを作り、線をつないで接続します。ステートは、独自のプロパティを持つトランジションを用いて接続します。トランジションは一方通行ですが、ふたつを互いに接続させあうことができます。

../../_images/animtree11.gif

トランジションにはいくつもの種類があります:

../../_images/animtree12.png
  • 即座 (Immediate): 次のステートへと即座に切り替わります。現在のステートが終わると、新しいほうの先頭から始まります。
  • 同期 (Sync): 次のステートへと即座に切り替わりますが、新しいステートでも古いステートと同じ再生位置になります。
  • 終わりに (At End): 現在のステートの再生位置が最後になるまで待ち、それから次のステートの先頭に切り替わります。

トランジションはまた、いくつかのプロパティを持ちます。トランジションをクリックすると、インスペクタ ドックに表示されます:

../../_images/animtree13.png
  • Switch Mode はトランジションの種類です (上記参照)。作成した後でもここで変更できます。
  • Auto Advance (自動移行)は、このステートに到達したらすぐにトランジションがオンになります。これは At End スイッチモードと合わせるのが最適です。
  • Advance Condition (移行条件) は、この条件が満たされた場合に自動移行させます。テキストフィールドには変数名を入れます。この変数はコードから変更することができます (詳しくは後述)。
  • Xfade Time は、このステートと次との間でのクロスフェードにかかる時間です。
  • Priority (優先度) は、コード内でのtravel()関数と共に使用します (詳しくは後述)。ひとつのステートから別へと移動するときの、このノードの優先度を設定します。
  • Disabled (無効) は、このトランジションを無効にします (ただし travel や自動移行では無視されます)。

ルート モーション

3Dアニメーションの作業では、スケルトンを動かすためにルート・スケルトン ボーンを用意するのが一般的なテクニックです。これにより、キャラクターを床に合わせて歩かせることが可能になります。また、ムービー場面においても、正確に物体に触れさせられるようになります。

Godot内でアニメーションを再生するときは、このボーンを ルート・モーション トラック に指定することができます。そうすると、そのボーンの見た目の変化は打ち消されます (アニメーションはそのまま)。

../../_images/animtree14.png

そのあと、実際のモーションはAnimationTree APIからトランスフォームとして取得します:

anim_tree.get_root_motion_transform()
animTree.GetRootMotionTransform();

これをKinematicBody.move_and_slideなどの関数に渡すことで、キャラクターの動きをコントロールできます。

また、ツールノードとしてRootMotionViewがあり、シーン中に置くことでキャラクターとアニメーション用のフロアが表示されます (通常このノードはゲーム中は無効にされます)。

../../_images/animtree15.gif

コードからコントロールする

ツリーを作成してプレビューしたら、最後に残る質問は「これを全部コードからコントロールする方法は?」です。

注意していただきたいのは、アニメーション ノードはリソースであり、したがって、すべてのインスタンス間で共有されます。ノードの値を直接変更すると、このAnimationTreeを使用するシーンにある、すべてのインスタンスも影響を受けます。しかし、これにはクールな応用方法もあります。例えば、アニメーションツリーの一部をコピー&ペーストできますし、(ステートマシンやブレンドスペースのような)複雑なレイアウトのノードを、異なるアニメーションツリー間で再利用できます。

実際のアニメーション データはAnimationTreeノードに格納されており、プロパティを通じてアクセスします。AnimationTreeの Parameters セクションに、リアルタイムで変更できるパラメーターが全てあるので確認してみてください:

../../_images/animtree16.png

これらはAnimationPlayerや、AnimationTree自身からすらもアニメーション化できるので便利です。これで非常に複雑なアニメーション ロジックを実現できます。

これらの値をコードから変更する場合は、プロパティ パスを知る必要があります。それには、パラメーターの上にマウスをホバーするだけです。

../../_images/animtree17.png

そうすれば、それらを設定したり読み込めます:

anim_tree.set("parameters/eye_blend/blend_amount", 1.0)
# Simpler alternative form:
anim_tree["parameters/eye_blend/blend_amount"] = 1.0
animTree.Set("parameters/eye_blend/blend_amount", 1.0);

ステートマシン travel

GodotのStateMachine実装にあるナイスな機能のひとつは、travel (移動)です。現在のステートからの移動を指示すると、グラフは中間のものを全て通ってから、別のステートへと移ります。これはA* アルゴリズムにて行われます。

travel機能を使うには、まずAnimationTreeノードからAnimationNodeStateMachinePlayback オブジェクトを取得します (プロパティのひとつ)。

var state_machine = anim_tree["parameters/StateMachine/playback"]
AnimationNodeStateMachinePlayback stateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/StateMachine/playback");

取得したら、その多くの関数の中からひとつ呼び出せば使えます:

state_machine.travel("SomeState")
stateMachine.Travel("SomeState")