ゴマちゃんフロンティア

気まぐれと勢いで作るUnityゲーム開発日記です

【再編集】Unity開発メモまとめ「Animator」

time 2015/09/24

※本記事は旧ブログの記事を再編集し、再度投稿したものです。
 一部内容に差異がある可能性がありますので、予めご了承ください。

【2016/05/29】
記事内容を一部修正・加筆しました。
Unity5.3.3 時点の内容になります。

スクリプト系メモ

Animatorのスクリプト制御に関するメモ集です。

Animator.Play()

引数で指定したアニメーションを再生します。
どのステートからでも強制的に遷移されます。
「攻撃をキャンセルして回避」などの割り込みを AnimatorController 側のパラメータで制御するのはなかなか大変です。
素直にスクリプト側でこれを使えば即時に遷移してくれます。

Animator.CrossFade()

こちらも引数で指定したアニメーションを再生しますが、遷移に掛かる時間を指定することができます。
AnimatorControllerのステートのインスペクターにある「transitionDuration」そのままだと思われます。

自分がCrossFade()の方が便利だと思うケースは、「アニメーションの途中から再生」させる場合です。

Input.GetButton("Jump") {
    animator.CrossFade("Jump", 0, 0, 0.8f);
}

CrossFade()の第4引数で「normalizedTime」を指定できます。
遷移先ステートの開始時間を0~1の間で指定できます。
上の例の場合、「Jump」というアニメーションの80%の部分から再生します。
「ジャンプ開始モーションは要らないけど、後半の落下モーションから再生したい」といった場合に便利です。

Animator.ResetTrigger()

指定したトリガーの状態をリセットします。
たまにリセットされない場合があるので、StateMachineBehaviourと合わせて使うと効果的です。
StateMachineBehaviour については後述します。

public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    animator.ResetTrigger("Jump");
}

HasExitTime

ステートのインスペクターに存在する項目です。
チェックを入れるとモーションが終わるまで他のステートに遷移しなくなります。
途中で他のステートに遷移させたくない場合はチェックを外しておきましょう。
Unity5 から Condition の項目から独立しているため注意します。

shot2ss20160529121715066

ただしAnimator.Play()Animator.CrossFade()等で遷移させる場合、モーション途中でも遷移します。
AnimatorController とスクリプトの両方から制御している場合は注意が必要です。

EntryノードとExitノード

Unity5 の AnimatorController に追加されたノードです。
ステートマシンはEntryノードから遷移が始まり、Exitノードへ流れていきます。

遷移先にステートマシン自体を選択することで、そのステートマシンのEntryノードへ遷移させることができます。
サブステートマシン内のExitノードに到達した場合、遷移は1つ上のステートマシンのEntryに戻ります。

・・・という趣旨のものですが、正直かなりとっつきにくいです。
特に遷移先にサブステートマシンを指定した際に、ステートマシン内のEntryノードからどのステートへ流れるかが把握しにくいです。
(私的には)スクリプトで制御した方がスッキリしたりするので、無理に意識する必要もないかもしれません。
(サブステートマシン自体は類似するステートをまとめて管理できるので便利です)

一応公式のチュートリアルもありますが、英語な上メリットが伝わってこないので厳しいですorz
上手く使えば機能のカプセル化が実現できるようですが・・・。

https://www.youtube.com/watch?v=lpekqN4_4xg

StateMachineBehaviourを使った制御

StateMachineBehaviour は Unity の機能名ではなく、API のクラス名になります。
このクラスを継承したスクリプトは、Animator のステートに付けることができるようになります。
ステートに付与することで、特定のタイミングでコールバック関数が実行されます。

普通のスクリプトと同じようにパラメータも設定できます。
ただし特定のオブジェクトを指定するようなパラメータを持つことはできません。
(複数のキャラで使われる場合に使いまわせるようにするためらしいです)

ただ、コールバック関数実行時に引数として Animator が渡されるため、それでGmaeObjectを参照すればいろいろできます。
Player クラスにゲッタやセッタを用意しておけば、間接的に値を操作することができ、SendMessage()で関数も実行できちゃいます。
例えば以下の例の場合、ステート終了時にプレイヤーのゲームオブジェクトのdamageReset()関数を呼び出します。

public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
    animator.gameObject.SendMessage("damageReset", 0f);   
}

実装は StateMachineBehaviour の該当メソッドをオーバーライドして使います。
自動的に base が挿入されますが、消してしまっても問題ないと思われます。

汎用クラスの作成

ステートの遷移をコントロールする汎用クラスを作成します。
あくまで条件なので、「ジャンプ」「攻撃」に伴う処理(攻撃判定など)は各々のクラスで実装します。

以下のソースはボタン押下時にTriggerを操作したり、Animator.Play()を実行することで遷移させるものです。
InputManagerの名前とinputKeyを対応させる必要があります。

namespace StateController {
     public class SwitchTriggerByKey : StateMachineBehaviour {
 
          [SerializeField]
          private string inputKey;
 
          [SerializeField]
          private string parameterName;
 
          [SerializeField]
          private bool useAnimatorPlay;
 
          public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
               if (Input.GetButtonDown(inputKey)) {
                    switchTrigger(animator);
               }
          }
 
          private void switchTrigger(Animator animator) {
               if (useAnimatorPlay) {
                    animator.Play(parameterName);
               }
                    animator.SetTrigger(parameterName);
               }
          }
     }
}

2段ジャンプの実装

自分のゲームではプレイヤーキャラクターの制御に「CharacterMotor」を使用しています。
基本的な移動やジャンプなどが簡単に実装できますが、「ジャンプ後は再着地までジャンプできない」といった部分まで厳密なので、
2段ジャンプなどの特殊な移動を行う際は工夫が必要です。

そこで StateMachineBehaviour を使ってみます。
「ジャンプ中に再度ボタン押下」で2段ジャンプ用ステートに遷移するようにします。
2段ジャンプのステートに以下のスクリプトを付与し、Enter 内でジャンプを行います。
ジャンプの移動処理はiTween.MoveBy()で行います。

 
public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
     animator.gameObject.GetComponent<charactermotor>().movement.velocity.y = 0;
 
     iTween.MoveBy(animator.gameObject, iTween.Hash(
          "amount", new Vector3(0f, 25f, 0f),
          "time", 2f)
     );
 
     animator.ResetTrigger("Jump");
}

2段ジャンプ用のステートに遷移された瞬間にOnStateEnter()が実行されます。
animator.gameObjectがプレイヤーのゲームオブジェクトになるので、これに対してiTweenを実行します。
amount と time の値はゲーム内容に合わせてお好みで。
2段ジャンプ時はCharacterMotor.movement.velocity.yの値を0にし、Y軸に余計な速度が掛からない状態で実行するようにします。

これに限らず「特定のモーション中に○○がしたい」といった際に便利です。
プレイヤーキャラのUpdate()で判定するよりも確実で、Player クラスのコードもスッキリします。

AnimatorOverrideController

1つの AnimatorController をベースに、別々のモーションを割り当てられる機能です。
「キャラクター間でコントローラは同じだけどモーションが違う」といった場合に便利です。
ベースとなる AnimatorController を選択し、それぞれのモーションを割り当てていきます。

shot2ss20160529134240605

キャラクター毎に固有のアクションをさせたい場合には不向きかもしれません。
逆にアクションが共通化されている場合は非常に便利です。

スポンサーリンク

down

コメントする



ツイッター