Unity

StateMachineBehaviourを使ってみて気づいたこと(GodControllerを修正)

【GodController】スマホのマルチタッチに対応したバーチャルコントローラーを作ろう!(Unityエディタでも確認可能) https://github.com/okamura0510/GodController めっちゃ久しぶりのブログですw ...

こちらの記事でAnimatorの終了を検知するのに苦戦したのですが、

StateMachineBehaviour

こいつを使えば良さそうだと気づきました!
なので実際に修正してみました。

StateMachineBehaviourについて

Animatorのステートを検知できるコンポーネントですが、詳しくは以下をご確認ください。

Unity5 MecanimのStateMachineBehaviourと戯れる
Animatorのステート(状態)の変更を検知する StateMachineBehaviour【Unity】

修正内容

  • Animatorでモーションが終わったら処理を行いたい
  • モーションが開始してからモーション終了を検知しないと上手くいかなかった

という状況でUpdateでモーション終了を監視していたのですが、

旧:UnityChan.cs

public class UnityChan : MonoBehaviour
{
    void Update()
    {
        // 終了モーションがある場合は監視し、終わったら初期化する
        // モーション開始と終了をスクリプトで監視してるけど、本当は Animator 任せにした方が良い(安定しない。。)
        if(endMotion != "" && animator.GetBool(parameter))
        {
            var state = animator.GetCurrentAnimatorStateInfo(0);
            if(!isPlayingMotion)
            {
                if(state.IsName(motion)) isPlayingMotion = true;
            }
            else
            {
                if(state.IsName(endMotion)) EndMotion();
            }
        }
    }
}

新:UnityChanMotion.cs

public class UnityChanMotion : StateMachineBehaviour
{
    public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // 終了モーションを検知したらモーションを停止させる
        if(endMotion != "" && animator.GetBool(parameter))
        {
            var state = animator.GetCurrentAnimatorStateInfo(0);
            if(state.IsName(endMotion)) Stop();
        }
    }
}

修正した結果

  • UpdateOnStateExitに変更したことで毎フレ監視する必要がなくなって負荷減少
  • モーション開始を検知する必要がなくなったのでコード量削減
    (適切にイベントを検知できるようになった)
  • UnityChanMotionにモーション関連を移したことでUnityChanクラスが小さくなる
    (コンポーネント指向)

って感じで良くなった気がします!

【注意点】StateMachineBehaviourはSerializeFieldでシーンにあるオブジェクトを参照できない

なのでこんな感じでスクリプト側で初期化しています。

UnityChan.cs

public class UnityChan : MonoBehaviour
{
    [SerializeField] Animator animator;
    UnityChanMotion motion;

    void Start()
    {
        motion = animator.GetBehaviour<UnityChanMotion>();
        motion.Init(animator);
    }
}

UnityChanMotion.cs

public class UnityChanMotion : StateMachineBehaviour
{
    Animator animator;

    // モーション初期化
    public void Init(Animator animator)
    {
        this.animator = animator;
    }
}

UnityChanMotion.cs

参考までにコード全文載せときます。

/// <summary>
/// ユニティちゃんモーション。モーション再生や、終了モーションの検知を行っている。
/// </summary>
public class UnityChanMotion : StateMachineBehaviour
{
    Animator animator;
    string parameter = "";
    string endMotion = "";

    public string Parameter => parameter;
    public string EndMotion => endMotion;

    // モーション初期化
    public void Init(Animator animator)
    {
        this.animator = animator;
    }

    // モーション再生
    public void Play(string parameter, string endMotion = "")
    {
        if(this.parameter == parameter) return;

        this.parameter = parameter;
        this.endMotion = (endMotion != "") ? $"Base Layer.{endMotion}" : "";
        animator.SetBool(parameter, true);
    }

    // モーション変化
    public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        // 終了モーションを検知したらモーションを停止させる
        if(endMotion != "" && animator.GetBool(parameter))
        {
            var state = animator.GetCurrentAnimatorStateInfo(0);
            if(state.IsName(endMotion)) Stop();
        }
    }

    // モーション停止
    public void Stop()
    {
        animator.SetBool(parameter, false);
        parameter = "";
        endMotion = "";
    }
}

最後に

StateMachineBehaviour、存在は知ってて実際の現場でも見たことあったんだけど、ちゃんと自分で使わないとわからないもんだね(´・ω・`)

【エビでもわかる】オセロプログラミング
〜オセロを作りながらゲームのプログラムを学ぼう〜
「Unityで初めてゲームを作ってみたい!」

そんな人のためにこの本を作りました。
オセロを一から作りながら実践形式でプログラムを学べる本です。
すでに完成したプロジェクトを説明するのではなく、実際に作りながら説明していきます。
一緒に手を動かしながら、プログラムを覚えていきましょう🌟