【Unity】複雑なアニメーション遷移を制御するAnimatorの作り方
昔こんな記事を書きました。
さらに、最近Gotanda.unity - connpass という集まりでもAnimatorについてのLTを行いました。
ここらへんで、皆一度は悩むであろうAnimatorのそれなりにマシな作り方、設計の仕方をまとめていこうと思った次第です。
Animatorの矢印が多くて困っている、どういう指標で作ればわからない
という方への手助けとなれば幸いです
既知の手法でAnimatorを作った際の問題点
まず、既知の手法でAnimatorを作るとこういった形になると思います
1つ1つAnimatorのステートを作り、それらをトランジションで繋ぐ作り方です
トランジションの制御にはそれぞれboolやTriggerといったパラメータを用意し、スクリプトでAnimator.SetBool()などを呼び出す...
ありがちですね
UIなどに使用するシンプルなアニメーションであれば、上記手法でも良いかと思います
しかし、キャラクターの制御などにこれをそのまま当てはめてしまうとこのようになっていくと思います
これは極端な例ですが、矢印とパラメータが量産され視認性が悪くなっていきます
視認性の悪さを改善できる、サブステートマシンという機能が存在します
サブステートマシンとは、複数のステートをまとめておくことのできる機能です
詳細な説明はこちらの方が詳しく載っていますので割愛します
サブステートマシンは良いグルーピングの手法ではありますが、この手法ではパラメータが量産されることを防止できません
また、グルーピングされた結果サブステートマシンの中の特定のステートから、他のサブステートマシンの特定のステートへ直接遷移する
などの手法はトランジションでは取りづらくなります
つまるところ、根本的な解決にはなっていません
必要な要件について
現在開発している3Dアクションゲーム、SLEEPWALKERでは
- 大量にあるステートの全てが自由に遷移を組むことができること
- ステートの追加、組み替えが容易であること
- 時間に応じたモーションブレンディング設定
- 時間に応じたエフェクト設定、etc
が必要でした
いろいろと試行錯誤し、このような非常にスムースな遷移を実現できました
具体的な設計、実装について
Animator部分
まず、Animator自体はこのようになっています
トランジション、パラメータは一切ありません
デフォルトのステートはIdle(待機)モーションになるように設定してあり、AnyStateやExitなどの機能も使用していません
遷移は全てAnimator.CrossFadeInFixedTime()を使用しています
これについてはこちらをご覧ください
遷移の方法について
遷移はStateMachineBehaviourを継承したスクリプトを全てのステートに付け、制御を行っています
StateMachineBehaviourを継承することで、現在のステートのNormalizedTime(アニメーション経過時間)を取ることができるため
経過時間に合わせた処理を行うことができるようになります
まず基底クラスを作成し、NormalizedTimeなどを取得しやすくしました
かなりレガシーコードです、整理もしてません。Rxを使用しているのと、TODOとか書いてあるので、設計自体はあまり参考にしないでください
ResetTime()メソッドは、NormalizedTimeはステートにいる限り加算され続ける仕様のため、ループ設定をした際にNormalizedTimeが0.0f~1.0fで見れるようにtimeを初期化する処理です
気をつけないとループするアニメーションの際に設定したエフェクトなどが再生されなくなるので気をつけましょう
この基底を使いこのような設定を行うことのできるスクリプトができました
遷移が可能なステートのenumを定義し、遷移優先度、遷移可能時間などを設定しています
ソースコードもそのまま乗せておきます、こちらもかなりレガシーなコードなのであまり参考にはせず、ニュアンスだけの理解にとどめてもらえると嬉しいです
今見てもシングルトン経由したりしてやべえコードだ・・・
上記のようにAnimatorでアニメーション時間に関連する処理を書いていますが
AnimatorのStateと連動したStateMachineを作成し、Animatorの外側でゲームに関連する処理を書いています
アニメーション時間に応じたその他の処理
攻撃判定発生スクリプト
こちらも時間軸で動作を定義しています
SE再生スクリプト
ループ設定などもできるようにしてあります
まとめ
というわけで、トランジション設定をStateMachineBehaviourのスクリプトに寄せ、アニメーション時間が必要な処理もまとめてぶちこんでいます
トランジション設定を作っていくのに比べて
- パラメータがない
- トランジション設定がない
ので視認性、拡張性に優れます
また、Unity2017で追加されたPlayableを使用した完全スクリプトベースの手法も存在しますが
- Animatorでステートの制御をぽちぽち作れること
- ステートをビジュアル化しておけること
- コード量の少なさ
で優位です
もちろん適材適所ですが、1つの指標になれば幸いです
宣伝
現在開発中のゲーム、SLEEPWALKERの体験版がweb公開されました!
よろしければぜひプレイしてみてください