アニメーションの適用
Spineには、アニメーションを適用するための2つのAPIが用意されています。
AnimationState API
AnimationStateは後で再生するアニメーションをキューすることや、アニメーション間でのミキシング(クロスフェード)、複数アニメを重ねて適用する(レイヤリング)など時間の経過を伴うアニメーションの適用をサポートします。
AnimationStateはステートフル、つまり処理状態を把握し、アニメーションを適用するためにアニメーションの回数やその他のパラメーターを保管します。1つのAnimationStateで複数のスケルトンのポーズをとることができますが、複数のスケルトンに全く同じポーズを取らせることは稀です。通常1つのAnimationStateインスタンスは各Skeletonインスタンスに使用されます。
AnimationStateはTimeline API上で構築され、逆再生を除くほとんどのアニメーション再生のニーズに対応できます。逆再生が必要な場合、Timeline APIを直接使用するか、Spine内でボックス選択スケーリングを使用してアニメーションを複製し、逆にしてください。
AnimationStateのupdate
メソッドは、最後に呼び出された時からの経過時間を取得してから内部のステートを更新します。apply
メソッドはスケルトンを取得し、適切なアニメーションを適用します。
.
// Every frame:
state.update(delta);
state.apply(skeleton);
ミックスタイム
AnimationStateインスタンスの作成にはAnimationStateDataを指定する必要があります。AnimationStateが現在のアニメーションを変更すると、AnimationStateDataに定義されているミックスデュレーションを使ってアニメーションを自動的にミックス(クロスフェード)するため、アニメーションを自然に移行できます。
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プロパティを使ってケース毎に設定できます:
entry.mixDuration = 0.6;
「Data」というサフィックス(接尾辞)に示される通り、AnimationStateDataはステートレス、つまり処理状態を把握しません。同じAnimationStateDataインスタンスを複数のAnimationStatesに使用できます。
トラック
トラックはアニメーションをレイヤで適用することを可能にします。各トラックはアニメ―ションと再生パラメータを保管します。トラックはゼロから昇順に番号が付けられます(トラックインデックスは、内部的には配列のインデックスです)。AnimationStateがスケルトンに適用されると、トラック番号が低いものから順にアニメーションが適用されます。
トラックには様々な用途があります。例えば、全てにキーを作成しないアニメーションを上位のトラックで再生し、キーが作成された下位のトラックのみをオーバーライドできます。トラック0は歩く、走る、泳ぐなどのアニメーションに設定し、トラック1は腕と銃の発射のみにキーを作成した射撃のアニメーションにするなどです。また上位のトラックにTrackEntry alphaを設定することで、その下のトラックとミックスすることが可能になります。例えば、トラック0を歩くアニメーションに設定し、トラック1を足を引きずるアニメーションに設定するなどです。プレーヤーがさらに怪我を負うにつれトラック1のアルファを増加するように設定すれば、足をもっと引きずるようにできます。
再生
アニメーションのトラック設定はsetAnimationを呼び出すことで行われます。これはトラック上の現在のアニメーションとキューされたアニメーションを指定したアニメーションに置き換えられます。ミックスデュレーションが前のアニメーションと現在のアニメーションの間で定義される場合、現在のアニメーションがミックスデュレーション中にミックスされるため、アニメーション間のトランジションがスムーズに行われます。
setAnimation
は色々な方法で再生をカスタマイズするTrackEntryを返します。
デフォルトでは、別のアニメーションが再生されるか、トラックがクリアされるまで、アニメーションが適用され続けます。特定の時間が経過した後にアニメーションを停止するには、TrackEntry trackEnd時間を設定します。
キューイング
次に再生するアニメーションをキューするには、addAnimationを呼び出します。これにより、現在または最後にそのトラックにキューされたアニメーションの後にそのアニメーションを再生するように設定できます。トラックが空の場合、setAnimation
を呼び出したのと同様になります。
addAnimation
は再生のカスタマイズに使用できるTrackEntryを返します。
空のアニメーション
アニメーションが空のトラックに設定されると、直ちに再生が開始されます。同様に、トラックがクリアされるとアニメーションの適用が停止されます。アニメーションをミックスインまたはアウトさせるには、空のアニメーション、すなわちタイムラインのないアニメーションを指定する必要があります。空のアニメーションはミックスデュレーションを設定するプレースホルダーとして使用されます。空のアニメーションを設定したりキューに入れたりするのに便利なように、setEmptyAnimationメソッドとaddEmptyAnimationメソッドが用意されています。
セットアップポーズからアニメーションをミックスインするには、以下のように空のアニメーションを設定し、ミックスインするアニメーションを追加してmixDurationを設定します:
TrackEntry entry = state.addAnimation(track, "run", true, 0);
entry.mixDuration = 1.5;
アニメーションをセットアップポーズにミックスアウトするには、ミックスデュレーションを指定して空のアニメーションを設定またはキューします:
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
を呼び出す際には注意が必要です:
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を呼び出す必要があります。
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
メソッドを持っています。
animation.apply(skeleton, lastTime, time, loop, events, alpha, blend, direction);