アニメーションの適用

Spineには、アニメーションを適用するための2つのAPIが用意されています。

AnimationState API

AnimationStateは後で再生するアニメーションをキューすることや、アニメーション間でのミキシング(クロスフェード)、複数アニメーションを重ねて適用する(レイヤリング)など時間の経過を伴うアニメーションの適用をサポートします。

AnimationStateはステートフル、つまり処理状態を把握し、アニメーションを適用するためにアニメーションの時間やその他のパラメーターを保管します。1つのAnimationStateで複数のスケルトンのポーズを取らせるもできますが、複数のスケルトンに全く同じポーズを取らせることは稀です。通常、各Skeletonインスタンスに1つのAnimationStateインスタンスが使用されます。

AnimationStateはTimeline API上で構築され、逆再生を除くほとんどのアニメーション再生のニーズに対応できます。逆再生が必要な場合、Timeline APIを直接使用するか、Spine内でボックス選択スケーリングを使用してアニメーションを複製し、逆にしてください。

AnimationStateのupdateメソッドは、最後に呼び出された時からの経過時間を受け取り、内部のステートを更新します。applyメソッドはスケルトンを受け取り、適切なアニメーションを適用します。

AnimationState state = ...
.
// Every frame:
state.update(delta);
state.apply(skeleton);

ミックスタイム

AnimationStateインスタンスの作成にはAnimationStateDataを指定する必要があります。AnimationStateが現在のアニメーションを変更すると、AnimationStateDataに定義されているミックスデュレーションを使ってアニメーションを自動的にミックス(クロスフェード)するため、アニメーションを自然に移行できます。

AnimationStateData stateData = new AnimationStateData(skeletonData);
stateData.setDefaultMix(0.1);
stateData.setMix("walk", "jump", 0.2);
stateData.setMix("jump", "walk", 0.4);
stateData.setMix("jump", "run", 0.25);
stateData.setMix("walk", "shoot", 0);
AnimationState state = new AnimationState(stateData);

ミックスデュレーションを全てのアニメーションペアに手動で設定する代わりに、デフォルトのミックスデュレーションを設定できます。またミックスデュレーションはTrackEntry mixDurationプロパティを使ってケース毎に設定できます:

TrackEntry entry = state.setAnimation(0, "walk", true, 0);
entry.mixDuration = 0.6;

「Data」というサフィックス(接尾辞)に示される通り、AnimationStateDataはステートレス、つまり処理状態を把握しません。同じAnimationStateDataインスタンスを複数のAnimationStateに使用できます。

トラック

トラックはアニメーションをレイヤーで適用することを可能にします。各トラックはアニメ―ションと再生パラメーターを保管します。トラックはゼロから昇順に番号が付けられます(トラックインデックスは、内部的には配列のインデックスです)。AnimationStateがスケルトンに適用されると、トラック番号が低いものから順にアニメーションが適用されます。

トラックには様々な用途があります。例えば、一部の要素にだけキーを作成したアニメーションを上位のトラックで再生することで、キーが作成された要素だけを下位トラックにオーバーライドできます。トラック0は歩く、走る、泳ぐなどのアニメーションに設定し、トラック1は腕と銃の発射のみにキーを作成した射撃のアニメーションにするなどです。また上位のトラックにTrackEntry alphaを設定することで、その下のトラックとミックスすることが可能になります。例えば、トラック0を歩くアニメーションに設定し、トラック1を足を引きずるアニメーションに設定して、プレーヤーが怪我を負うにつれトラック1のアルファを増加するように設定すれば、足をもっと引きずるようにできます。

再生

アニメーションのトラック設定はsetAnimationを呼び出すことで行います。これはトラック上の現在のアニメーションとキューされたアニメーションを指定したアニメーションに置き換えます。ミックスデュレーションが前のアニメーションと現在のアニメーションの間で定義される場合、現在のアニメーションがミックスデュレーション中にミックスされるため、アニメーション間のトランジションがスムーズに行われます。

setAnimationは再生をカスタマイズする様々な方法を提供する「TrackEntry」を返します。

デフォルトでは、別のアニメーションが再生されるか、トラックがクリアされるまで、アニメーションが適用され続けます。特定の時間が経過した後にアニメーションを停止するには、TrackEntry trackEnd時間を設定します。

キューイング

次に再生するアニメーションをキューするには、addAnimationを呼び出します。これにより、現在または最後にそのトラックにキューされたアニメーションの後にそのアニメーションを再生するように設定できます。トラックが空の場合、setAnimationを呼び出したのと同様になります。

addAnimationは再生のカスタマイズに使用できるTrackEntryを返します。

空のアニメーション

アニメーションが空のトラックにセットされると、直ちに再生が開始されます。同様に、トラックがクリアされるとアニメーションの適用が停止されます。アニメーションをミックスインまたはアウトさせるには、空のアニメーション、すなわちタイムラインのないアニメーションを指定する必要があります。空のアニメーションはミックスデュレーションを設定するプレースホルダーとして使用されます。空のアニメーションをセットしたりキューに追加したりするには、setEmptyAnimationメソッドやaddEmptyAnimationメソッドを使用できます。

セットアップポーズからアニメーションをミックスインするには、以下のように空のアニメーションを設定し、ミックスインするアニメーションを追加してmixDurationを設定します:

state.setEmptyAnimation(track, 0);
TrackEntry entry = state.addAnimation(track, "run", true, 0);
entry.mixDuration = 1.5;

アニメーションをセットアップポーズにミックスアウトするには、ミックスデュレーションを指定して空のアニメーションを設定またはキューします:

state.setAnimation(track, "run", true, 0);
state.addEmptyAnimation(track, 1.5, 0);

アニメーションがTrackEntry trackEnd時間に達すると、そのアニメーション中でキーが作成されている各プロパティにセットアップポーズを設定し、トラックがクリアされます。即座にセットアップポーズに戻す代わりに、setEmptyAnimation または addEmptyAnimation を使用してスケルトンをセットアップポーズにミックスして戻す方が望ましい場合があります。

TrackEntry

アニメーションをセットしたり、キューに追加したりするメソッドはTrackEntryを返します。これは再生のカスタマイズに使用できます。TrackEntryで利用できる各種プロパティについては、TrackEntryのAPIリファレンスをご覧ください。

参照

TrackEntryへの参照を保持することも可能です。例えば、時間の経過とともにalphaまたはtimeScaleプロパティを調整することができます。しかしdisposeリスナーイベントの発生タイミングを過ぎても参照を保持してしまわないように注意する必要があります。

リスナー

アプリケーションはTrackEntryのライフサイクルイベントの通知を受けるためにコールバックを登録することができます。AnimationStateのaddListenerは全てのTrackEntryイベントにリスナーを登録します。また、特定のTrackEntryにリスナーを設定し、そのエントリーからのみイベントを受け取ることも可能です。

利用可能なイベントはAnimationStateListenerに記載されており、指定された順序で発生することが保証されています。イベントはAnimationStateの update または apply メソッドにより内部処理の間にキューされ、リスナーにはその後、メソッドが返る直前に通知されます。これにより、リスナーはアニメーションをセットしたりトラックをクリアするなどのAnimationStateの操作を安全に実行できます。しかしすでにキューされたイベントはclearListenerNotificationsが使用されない限りすべて発生します。

新しいアニメーションの設定など、リスナー内で行われたAnimationStateへの変更は、次にAnimationState applyが呼び出されるまでスケルトンに適用されません。これはリスナー内で行うことができますが、最初に update を呼び出すように注意する必要があります:

// Inside a listener:
state.setAnimation(0, "jump", false);
state.update(0); // Advance internal state.
state.apply(skeleton);

applyメソッドは内部の状態を変更せずに単一のAnimationStateを複数のスケルトンに適用することができます。updateを呼び出すことで、AnimationStateは、全ての適用が完了して後続のapply呼び出しが次のフレームで行われることを知ることができます。updateが呼び出されない場合、applyは同じリスナー通知を引き起こしてしまう可能性があり、無限ループやスタックオーバーフローの原因となります。

Timeline API

Timeline APIは、AnimationクラスとTimelineクラスにより構成されるアニメーション適用の最下層のAPIです。これらのクラスはステートレス、つまり処理状態を把握しないため、アニメーションを適用するための時間やその他のパラメータを外部に保存して操作する必要があります。このAPIはアニメーションの再生を最もコントロールしやすいですが、再生状態の管理に多くの作業を必要とします。そのため、ほとんどのユーザーはAnimationState APIを使用することを選ぶでしょう。

Animationは名前とTimelineのリストを持つ非常にシンプルなものです。各タイムラインが、スケルトンのプロパティを時間経過に伴いどのように変更するかを把握しています。アニメーションをスケルトンに適用するには、アニメーション内の各タイムラインにapplyを呼び出す必要があります。

time += delta;
alpha = 1; // For mixing between the current or setup pose (0) or the animation pose (1).
blend = MixBlend.first; // How the current or setup pose is mixed with the animation pose.
direction = MixDirection.in; // Whether mixing out to the setup pose or in to the animation pose.

for (Timeline timeline : animation.timelines)
   timeline.apply(skeleton, lastTime, time, events, alpha, blend, direction);

// The events list contains any events fired between lastTime and time.
// Process them here, then clear the list.
events.clear();

lastTime = time;

Animationはloopパラメータを持ち、各タイムラインでapplyを呼び出すだけの便利なapplyメソッドを持っています。

loop = true;
animation.apply(skeleton, lastTime, time, loop, events, alpha, blend, direction);

次: ランタイムスケルトン 前: スケルトンデータのロード