こまめにまとめるシリーズで、今回はアニメーション開始・終了のイベントについてです。
以下、SpineランタイムはSpine-Starlingの4.3を使います。どの言語のランタイムでも大きさな差はないと思われます。
AnimationStateのイベント
アニメーションの開始と終了イベントは、AnimationStateの各Listenersオブジェクト(後述)にコールバック登録する事で得る事ができます。コールバックに渡される引数はトラック番号および、onCompleteのみ第二引数にそのイベントの発行回数が渡されます。
var state:AnimationState = skeletonAnimation.state;
state.onStart.add(function(track:int):void{
trace("track#" + track + " のアニメが開始した");
});
state.onEnd.add(function(track:int):void{
trace("track#" + track + " のアニメが停止した");
});
state.onComplete.add(function(track:int, count:int):void{
trace("track#" + track + " のアニメが" + count + "回、完了した");
});
state.setAnimation...
onEnd
とonComplete
の差はなんでしょうか?
onCompleteは設定されたアニメーションが最後まで再生された時に発行されるイベントです。ループアニメの場合何度もonCompleteイベントが発行されるので、その回数として第二引数にカウンタ値も渡されます。非ループアニメの場合、一度だけこれが発行されます。
対して、onEndは再生中のアニメが止まった、または、止められた際に発行されます。clearTrack
命令による強制アニメ停止、次のアニメの上書き設定による停止、今のアニメが最後まで再生されて(=onComplete)の停止、、など理由はなんであれ、Trackの再生状態が解除された際に発行されます。onCompleteのタイミングでonEndも発行されます。
注意として、一つのトラックに連続アニメーションがキューに入って設定されている場合(こちらの記事参照)で、キュー全体でsetAnimation
やclearTrack
命令で破棄された場合は、再生中TrackEntry以外はonEndイベントが発行されないという点があります。
TrackEntryのイベント
AnimationStateでまとめてではなく、アニメーション再生指示毎にイベントを受けることもできます。具体的にはTrackEntryにコールバック関数を設定します。
var trackEntry:TrackEntry = animationState.setAnimationByName(0, "hoge");
trackEntry.onStart = function(track:int):void {
trace("track#" + track + " のアニメが開始した");
}
trackEntry.onEnd = function(track:int):void {
trace("track#" + track + " のアニメが停止した");
};
trackEntry.onComplete = function(track:int):void {
trace("track#" + track + " のアニメが" + count + "回、完了した");
};
イベントの種類と挙動はAnimationStateと同じですが、提供されるのがListenersではなく、素のFunctionである事に注意です。上記のソースでは、onStartにaddせずonStartを関数でそのまま上書きしています。コールバック関数設定を解除するには、それぞれにnullを代入します。
trackEntry.onStart = null;
trackEntry.onEnd = null;
trackEntry.onComplete = null;
Listenersとは?
FlashやStarlingのイベントモデルはEventListenerを継承・実装するクラス構成をとっていますが、そのEventListener機能をプロパティ側に抜き出してもたせような形式であるのがspine.animation.Listeners
です。addしてリスナ登録、removeでリスナ登録解除となります。
function onStart(track:int):void {
trace("start!");
animState.onStart.remove(onStart); // 解除
};
animState.onStart.add(onStart); // 追加
C#のイベントモデルとか、ActionScript3 Signals みたいなやつですね。EventListenerよりもスマートな実装で自分はこちらの形式の方が好きです。(Signalsについてはこちらのサイトが詳しいです。 http://www.inazumatv.com/contents/archives/5729 ) SpineのListenersはSignalsほど高機能ではないですが、Spineのランタイムライブラリ内部だけでなく、ユーザ側でも利用できるので、簡単なコードを書くときなどは利用できそうです。
連続再生用途にonEndを使わない
onEndイベントのハンドラ内(ListenersでもFunctionでも)にて次のアニメーション再生を行おうとすると、アニメーション制御がおかしくなる現象を手元で確認しました。アニメーションの連続再生用途にはaddAnimation
/addAnimationByName
を使うか、onCompleteハンドラを使うかしましょう。もしonEnd内部でなんとか次アニメーションを再生しなくてはいけないシチュエーションがあるなら、1フーム待ってから次のアニメーション指示をを出すようにしましょう。
おわりに
AnimationStateとTrackEntryでイベントの仕組みが異なっているのは、無数に作られていくTrackEntryにListenersは大げさすぎるという判断でしょうか。直接関数指定の方がパフォーマンス的にも良さそうなのでそこは良いのですが、どちらにしろコールバックへの引数にはランタイムがTrackEntry自体も追加で渡してくれるともっと便利な気はしますね。
以上です。では。