前書き
ゲーム開発していて、入力モードが増えてきて、ステート管理していきたいと思う中
Unity の State Machine って Mecanim の AnimatorController だなぁと気づき
デバッグ時にステートを遷移を可視化しつつ、これをスクリプトから操作や取得できたらいいなというのが
この記事のモチベーションです。
Animator からステート名は取れない
どうもステート名を外から取るには Asset をロードしなければならないとのことであきらめ
Enum 値とステート名のハッシュ値の関連から、なるべくコストをかけずに Enum 値として State を見極められるようにしたいと思います。
アイディアの具現化
次のような Explore(探索モード)を必ず通って各ステートへ切り替わるステートマシンを用意します。
トリガーはステート名を指定すると、そのステートへ移動する設定
これをスクリプト側で、現在のステートと次のステートへの移動を Enum で取り扱えるように工夫したものがこちら
using System.Collections.Generic; using UnityEngine; /// <summary> /// ゲーム内の入力モード・ステート管理 /// </summary> public class InputModeStateMachine : MonoBehaviour { void Start() { // ステートマシンを Layer 0 に持つ AnimatorController を持つ Animator を期待 this.animator = GetComponent<Animator>(); // 各ステート名は Enum 名と同じように作られていることを期待 for (InputModeState stateEnum = InputModeState.Explore; stateEnum < InputModeState.Max; stateEnum++) { var fullPath = INPUT_STATE_LAYER_NAME + "." + stateEnum.ToString(); var hash = Animator.StringToHash(fullPath); this.stateHashToEnum.Add(hash, stateEnum); } } // Update is called once per frame void Update() { // スペースキーを押すと、ステートが切り替わる if (Input.GetKeyDown(KeyCode.Space)) { // 現在のステート情報 var stateInfo = this.animator.GetCurrentAnimatorStateInfo(INPUT_STATE_LAYER_INDEX); // ハッシュ値から Enum 値へマップ var currentState = this.stateHashToEnum[stateInfo.fullPathHash]; Debug.Log(currentState.ToString()); // Explore を中継にステートを切り替える(デバッグ機能) if (currentState != InputModeState.Explore) { this.animator.SetTrigger(InputModeState.Explore.ToString()); Debug.Log(InputModeState.Explore.ToString()); } else { this.animator.SetTrigger(nextState.ToString()); Debug.Log(nextState.ToString()); this.nextState = this.nextState + 1; if (InputModeState.Max == this.nextState) { this.nextState = InputModeState.Explore + 1; } } } } /// <summary> /// ステート名 /// </summary> public enum InputModeState { Explore = 0, Inventory, Mining, TextChat, Max } // デバッグ用 InputModeState nextState = InputModeState.Inventory; /// <summary> /// ステート名HashからステートEnumへのマップ /// </summary> Dictionary<int, InputModeState> stateHashToEnum = new Dictionary<int, InputModeState>(); /// <summary> /// レイヤー名(fullpath でプリフィックスに利用) /// </summary> const string INPUT_STATE_LAYER_NAME = "InputStateLayer"; /// <summary> /// ステートマシンのレイヤーインデックス /// </summary> const int INPUT_STATE_LAYER_INDEX = 0; /// <summary> /// ステートマシンのコントローラを持つアニメータ /// </summary> Animator animator; }