読者です 読者をやめる 読者になる 読者になる

テラシュールブログ

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

【Unity】Animatorのスパゲティなステートマシンを整理する

UnityのAnimatorはノードベースのステートマシンでアニメーションを制御する機能を持ちます。しかしながら、どんどんノードを繋げていくと昨日の記事のように複雑なグラフ図が出来上がってしまいます。

その辺りを踏まえて、自分なりに「こうすれば良かろうなのだ」みたいなのを考えてみます。

 前回の3形態に変形するロボを例として、ちゃんと手直ししていきます。

ノードを整理する

このノード軍が複雑な理由は一つ、お互いに参照しあっているノードが存在する事です。このロボは飛行機・戦車・ロボに変形し各形態で各々のアクションが存在するので、非常に複雑なフローが出来てしまっています。

f:id:tsubaki_t1:20151220110821j:plain

これを各モード毎のサブステートでまとめてしまいます。やり方はサブステートマシン作ってソコへ各ステートをドラッグ&ドロップです。

f:id:tsubaki_t1:20151220111451g:plain
ただし、このままでは他のサブステートへ直接接続しており参照関係が非常に複雑になってしまうので、一旦サブステートの持つステートへの接続は破棄し、各サブステート間でつなぎ直します。

f:id:tsubaki_t1:20151221012838j:plain

f:id:tsubaki_t1:20151221012844j:plain

なお、サブステートからステートを取り出すには、親レイヤーを表すステート(親がBase LayerならばBaseLayer)へドラッグ&ドロップすればOKです。

f:id:tsubaki_t1:20151221021055j:plain

パラメータ

この制御に使用していたTriggerは、実は少しに扱いにくいパラメータです。Triggerの特性は「一度通ったらOFFになる」なので、想定外の動作を行う事があります。
例えば、Triggerを事前に打っておいたら、トリガーが動くタイミングになったらいきなり動いてしまった…なんてこともあり得ます。
ジャンプとスライディングをAnimatorのTriggerで管理している場合、スライディング・ジャンプと押すと、スライディングが終わると即ジャンプが発動する…なんて事もあり得ます。

f:id:tsubaki_t1:20151221023616j:plain

他にも、Planeモード時にTankへの変形を行い、その途中でRobo→Planeの順番でTriggerを入力したらどうなるか。初期のステートではPlaneはTankに変形後、再び変形する訳ですが、アニメーションの順番はPlane→Roboの順番です。この食い違いは単純に実行優先度がPlane>Roboだったからです。

f:id:tsubaki_t1:20151221022833j:plain

等々、いろいろとあるので個人的にはモードやステートといった物はIntやBoolで管理するのが良いかなと思っています。
そうして善かれと思って作ったパラメータがこちら。enumがあればもっとクールでしたが、無いので運用でカバー。

f:id:tsubaki_t1:20151220112829j:plain

トランジションはこんな感じ

f:id:tsubaki_t1:20151221030700j:plain

EntryとExit

ステート内での収支はEntryとExitにお任せします。
このEntryとExitはは、サブノードの開始時にEntryから入り、Exitに届くとサブノードから出て判定を行います。サブノード単位で制御を管理していると、EntryとExitを使って制御が出来る訳です。

例えば、戦車モードから飛行機モードへ変形してみます。
Tankのサブステートから開始です。まずはTankでモードがPlaneになったので、TankToPlaneステート(変形するアニメーション)を通り、Exitへ出ます。

f:id:tsubaki_t1:20151221014507j:plain

いったんTankサブステートから抜け、RobotかPlaneかどちらかへ移行します。今回はPlaneモードを指定しているのでPlaneサブステートへ。

f:id:tsubaki_t1:20151221014517j:plain
Planeサブステートでは特に何かを指定されていないので、Entryから入り、Planeへ着地します。

f:id:tsubaki_t1:20151221014621j:plain

これはこれで上手く動くのですが、ここにキャンセルが入ると途端に面倒くさい事になります。つまり、モーションをキャンセルして他のモーションへ移行するケースです。

それが勝利ポーズといった特殊なアクションならば、何処からでも条件が揃えば呼べるAnyStateを使うのが妥当だと思いますが、キャンセル技のような物ならば素直にAnimator.PlayAnimator.CrossFadeを使用してステートをスクリプトでジャンプしてしまった方が理に適ってるような気がします。*1勿論ゲームにも寄りますが。

ステートのフォーカスが勝手に動かないように

最後にちょっとしたTipsですが、今回のようにサブステートへビュンビュン飛ぶ場合、どのサブステートに居るのか確認したい場合があります。
そんな時はAnimatorのAuto Live Linikを外して、アニメーションを追わないようにします。

f:id:tsubaki_t1:20151221025610j:plain
Auto Live Linikが外れている場合でも、現在のステートを持つサブステートが青くなるので、追跡は割と簡単に出来ます。

f:id:tsubaki_t1:20151221025751j:plain

関連記事

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

unity3d.com

 
 

*1:Unity 5.3.1でCrossfade中にCrossfade出来ない問題も解決しましたし。