テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】Animatorのアニメーション終了待ちをする方法

今回はAnimatorを使用している時にアニメーション再生が完了した事を検出する方法について考えてみます。

f:id:tsubaki_t1:20160211011044j:plain

アニメーション終了検知

もしUIをアニメーションで検出したり、キャラクターの攻撃後にエフェクトを表示といった事をしたい場合、アニメーションの終了「後」のタイミングで何らかの処理を挟むといった事が必要になります。

f:id:tsubaki_t1:20160211005154g:plain

さてAnimatorコンポーネントはAnimationコンポーネントと異なり、アニメーションを再生・管理するためにRuntimeAnimatorControllerを経由して管理しています。またアニメーション再生管理をステートマシンによって管理しているため、直接管理する事が出来ない糞仕様となっています。

AnimationコンポーネントであればシンプルにAnimation.IsPlayingでアニメーションの終了を検出できますが、Animatorではどうでしょう。とりあえず4つのパターンが考えられます。

現在のステートのnormalizedTimeを見る

まずは現在のステートのnormalizedTimeを取得するアプローチです。normalizedTimeはアニメーション開始時を0・再生後を1とするようにアニメーションの長さを正規化したものです。アニメーションの再生状態はnormalizedTimeが0からスタートし、最終的には1になる訳です。

つまり、アニメーションが再生完了している状態はnormalizedTimeが1以上になります。この部分を利用して、アニメーションの再生終了を知ります。

f:id:tsubaki_t1:20160211011641j:plain

ステートを取得するには、「どのレイヤーのステートなのか」という情報が必要になります。殆どの場合ステート0(≒ Base Layer)が該当します。

後はanimator.GetCurrentAnimatorStateInfo (レイヤー番号).normalizedTime のような形で現在再生中のステートのnormalizedTime が1以下なら再生中…のようなコードを記述してやればOKです。

f:id:tsubaki_t1:20160211012622j:plain

このアプローチで2点注意すべきは、アニメーションがループしている場合ステートがAnimatorのグラフにより移動する場合です。

ループはそもそもアニメーション再生完了の概念が無いので置いておくとして、ステートが移動する場合は何とかしたい所です。

f:id:tsubaki_t1:20160211013159j:plain

ステートを比較してアニメーションの終了を知る

アニメーションが終了しステートの移動が行われる場合、2つの検出方法が考えられます。一つは再生中のステートを保持しておき、現在再生中のステートと比較する、もう一つはステートの切り替わりを検出するです。

再生中のステートを保持しておき比較するアプローチは割と概念としては簡単です。animator.GetCurrentAnimatorStateInfo (レイヤー番号).fullPathHashで再生中のアニメーションのハッシュ値*1をキャッシュしておき比較すればOKです。

ここの罠としては、再生直後に現在のステートが切り替わる訳ではない…という微妙に面倒くさい点です。たとえPlayで即時切替しても取得するステートが切り替わっているのは1フレーム後となります。

 

もう一つの方法は、StateMachineBehaviourを使用する事です。StateMachineBehaviourはステートの移動を検知できるので、そこに処理を挟んでおけばステートの移動を検出する事が出来ます。

f:id:tsubaki_t1:20160211015441j:plain

ただ、StateMachineBehaviourに制御を記述すると色々と面倒くさいので、ExecuteEvents.ExecuteでMonobehaviourへメッセージを投げるようにしておくと良い感じになりそうです。

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

ステートが移動する…について

なおアニメーションが終了した際に移動するのは、Has Exit Timeの効果です。
ここで面倒くさいのが、Exit Timeが1でないと途中からアニメーション切り替えを始めます(初期値は0.7付近)。この設定は3Dモデルならモーションの終わりから次のモーションへ綺麗に繋いでくれるのですが、2Dスプライトアニメーションだと「終わり付近のスプライトアニメーションを再生せずスキップ」したりします。
とりあえず2Dスプライトアニメーション向けに使用する場合、Exit Timeは1でTransition Durationは0.こうしとけば多分大丈夫です。

f:id:tsubaki_t1:20160211015715j:plain

 コルーチンと組み合わせる

最後に、「アニメーション再生が終わったら云々」を行うためカスタムコルーチンと組み合わせます。
下のコードではWaitForAnimation を新しく定義し、ここでアニメーションが終了していればテキストを変更…といった感じの処理を実装しています。

gist.github.com

blogs.unity3d.com

関連

tsubakit1.hateblo.jp

 
 

*1:Aniamtorはステートは文字列ではなくハッシュ値で保持しているらしい