WaitForAnimationを自分用にカスタマイズしてみました。

スクリプトでAnimationの待機を検知したい場合、

コルーチンを使うのが良さげみたいです。

ただ、UnityのデフォルトにはAnimatorで設定したアニメーションが終わるまで待機する

というのが無いっぽいんです。*1

そこで検索してみた所、

テラシュールブログさんの方で、カスタムコルーチンを使ったAnimationの検知方法が記載されていました。

tsubakit1.hateblo.jp

これは便利そうだと、cacaponも使ってみたのですが、思ったように動かない…


そもそも、AnimationとかAnimatorの仕組みがよくわかってない部分もあるので、

今回はAnimatorで設定したアニメーションがどう変化しているのか?

それを調べたうえで、cacaponなりに自分で使える形にカスタマイズしましたので、

それを紹介しようかなと思います。

確認したUnityのバージョン

2020.3.3f1

準備編 Animationを準備する

こんな感じのものを準備しました。

f:id:cacapon:20210709105910g:plain

イメージの色を変えるだけの簡単なアニメーションです。

60フレームかけて変化するアニメーションと、

90フレームかけて変化するアニメーションを

ボタンを押すと再生されるようにしています。

アニメーションの詳しい設定方法については先週私がまとめたものもありますし、

cacapon.hatenablog.com

他のブログさんでも分かりやすくまとめている方がいるのでここでは割愛しますね。

調査① 元々の作りはどんなもの?

テラシュールブログさんが作成したWaitForAnimationは以下のようにして検知しているようです。

  • newしたタイミングのステート*2ハッシュ値を取得する
  • keepWaiting中は常に現在のステートのハッシュ値を取得する。
  • newしたタイミングのステートと現在のステートが同じであり、かつ 再生時間中なら待機する。*3

呼び出し側は、当人も不本意ながら*4のようですが、

アニメ再生後に、yield return null;で1フレーム進めることで、

newしたタイミングが待機したいステートになるように調整し、

狙いのステートが終わるまで待機するコルーチンを実現しているようです。

調査② cacapon環境で使ってみたけどうまく行かない…

使い方も分かったので、先ほどのアニメーションで試してみたのですが、うまく行きませんでした。

私が今回作ったアニメーターは以下の形式なのですが、

f:id:cacapon:20210709111942p:plain

どうも、1フレーム進めても待機状態に当たるNew Stateを取得しているようなのです。

どうすれば、Wait60,Wait90の部分だけ検知することができるのでしょう…

調査③ ステートのハッシュ値を調べてみる。

Animatorの現在のステートのハッシュ値

Animatorのインスタンス.GetCurrentAnimatorStateInfo(レイヤー番号).fullPathHash;

で取得することができます。

ならば、カスタムコルーチン内部でfullPathHashをログで出すようにしたら分かりそうです。

そこで、本来の使い方ではないでしょうが、カスタムコルーチンを常にTrueを返すようにし、

ボタンを押されてからのハッシュ値の変化を調べてみることにしました。

結果的には下記のようになりました。

  • Newされたタイミングから暫くHash値Aが続いている
  • 途中でHash値が変化する。
  • 再生されたアニメーションによってハッシュ値が変わっている。(60Fアニメをハッシュ値B、90Fアニメをハッシュ値Cとしました)
  • しばらくするとHash値Aに戻った。
  • ハッシュ値Bとハッシュ値Cにて明らかに時間差を感じた。

図にするとこのような形でしょうか?

f:id:cacapon:20210709121807p:plain

調査結果

私の環境で1Fずらしても上手くアニメーションが行かなかったのは、

最初の部分のHashAから抜け出せなかったがゆえにのようです。

そして、今回のアニメーションのようにExitで抜ける形であるならば、

HashAからHashXに変わったタイミングから、

HashXからHASHAに変わるタイミングを検知さえできれば

コルーチンによるAnimationの待機処理は実現できそうなのが分かりました。


ですので、この結果をもとに自分用にカスタマイズしたカスタムコルーチンを作成したいと思います。

実装してみる。

作ったものはこうなりました。

Cacaponカスタマイズ版

大きな変更点としては、normalizedTimeをオミットしたこと、

bool値 isPlayで再生開始と終了を検知できるようにしたことでしょうか。


newしたタイミングが恐らくIDLE中のハッシュ値というのを利用して、

変わったら再生開始、戻ったら再生終了、という判定を、

keepWaitingの中で行っています。


自分で使った範囲だと、正しく待機しているようです。*5

注意点

私がテラシュールブログさんの記事内に掲載されていたスクリプトを参考にしたように、

今回作成したアニメーションの待機処理を自己責任の上で参考に頂いて構いませんが、注意点があります。

それは、今回作成したアニメーション待機処理があくまで

Entry → 待機 → 再生 → Exit

という構成を取っているアニメーターを想定しているものということです。

他の構成が未確認ではありますが、

Entry → 待機 → 再生1 → 再生2 → Exit

のような構成なら、再生1、再生2の分だけ待機するでしょうし、

Entry → 再生1 → Exit

のような構成ならWaitForAnimationは永遠にTrueを返しますので

その点をご注意の上、参考頂ければと思います。

終わりに

限定的ではありますが、自分に合ったアニメーションの待機スクリプトを作成することが出来ました。

分からないことでも、調査することで理解が深まった良い機会になりました。

当面はUnityのゲーム作成を続けていると思いますので、

実装に当たって得た知見を引き続き公開できればなと思います。


それではまた。

参考リンク

大元のスクリプトを作成した方です。ありがとうございました。 tsubakit1.hateblo.jp

*1:Animation自体はisPlay?みたいなのがあるらしいですが、Animatorは無いみたいです

*2:Animatorでいう四角のやつ。

*3:normalizedTimeが0~1の間なら再生中扱いだそうです。詳しくは参考リンクにて

*4:コメントに解せぬと書いてありました

*5:冒頭のデモシーンのコンソール表示はカスタマイズしたWaitForAnimationの前後にログを挟んで表示したものになります。
これを見る限り正常に動作していそうです