9
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

UnityのAnimatorで、StateMachineBehaviourを分離せずに制御してみる

あんまり調べても出てこなかったのでメモがてらに投稿しました
Animator周りの参考に少しでもなれば幸いです

StateMachineBehaviour

UnityにおけるAnimatorのステートのイベントを管理するコンポーネントです
主にAnimatorのステートにアタッチして使ったりします
OnStateEnter、OnStateExitといったステート周りのタイミングを管理しており、コールバックで各イベント時の処理を行うことができます
このサイトがわかりやすいので読んでみるといいと思います

Unity5 MecanimのStateMachineBehaviourと戯れる
テラシュールブログ
http://tsubakit1.hateblo.jp/entry/2015/05/01/032208

Animatorのステートにアタッチするので個人的に処理が分散しがちかなーと
今回はこの問題を解決するために少しだけ工夫してみました

StateMachineBehaviourを制御する

ここからが本題なんですが、 実はStateMachineBehaviourってリファレンスで普通に取得できます
それがGetBehaviour、使い方はGetComponentと一緒です
例えばTというStateMachineBehaviourを継承したものを用意した場合は
T hoge =Animator.GetBehaviour<T>();
T[] hoges = Animator.GetBehaviours<T>();

みたいな感じで
GetBehaviourはアニメーター全体からBehaviourを取得するため、任意のBehaviourで都合よく取得したい場合などで難しい場合があります、その辺は注意です

なんで下みたいな感じでステートにはアタッチするけど、処理自体はMonoBehaviourで管理するみたいなこともできます!
まずはMonoBehaviourにアタッチするスクリプトです

TestModel.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestModel : MonoBehaviour {

    [SerializeField]
    private Animator _animator;

    private TestBehaviour[] _behaviour;

    private void Start()
    {
        _behaviour = _animator.GetBehaviours<TestBehaviour>();

        for (int i = 0; i < _behaviour.Length; ++i)
        {
            _behaviour[i].SetStateEnterEvent(Test);
            _behaviour[i].SetStateEnterEventWithState(InfoTest);
        }
    }

    private void Test()
    {
        Debug.Log(_animator.GetCurrentAnimatorClipInfo(0)[0].clip.name);
    }

    private void InfoTest(AnimatorStateInfo info)
    {
        Debug.Log(info.length);
    }
}

今回はBehaviourを全部取得してすべてのイベントを共通で登録しました
もちろん単体で取得しても、別々のBehaviourを設定してイベントを登録しても大丈夫です

続いては肝心のStateMachineBehaviour側です

TestBehaviour.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TestBehaviour : StateMachineBehaviour {

    // ステートに入ったときのイベントリスナー
    private Action OnStateEnterListener;

    // ステートに入ったときのイベントリスナー
    private Action<AnimatorStateInfo> OnStateEnterWithStateListener;

    // イベント設定
    public void SetStateEnterEvent(Action action)
    {
        OnStateEnterListener += action;
    }

    // イベント設定
    public void SetStateEnterEventWithState(Action<AnimatorStateInfo> action)
    {
        OnStateEnterWithStateListener += action;
    }

    //StateMachineBehaviourのステート遷移時のコールバック
    public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        if (OnStateEnterListener != null)
        {
            OnStateEnterListener();
        }

        if (OnStateEnterWithStateListener != null)
        {
            OnStateEnterWithStateListener(stateInfo);
        }

        base.OnStateEnter(animator, stateInfo, layerIndex);
    }
}

見ての通り、リスナーを用意し、外部からの登録を可能にしています
これを呼んであげればいいだけです

今回はTestModelをUnityちゃんにアタッチして、TestBehaviourを各ステートにアタッチします

実行して適当にアニメーション再生してみた結果がこんな感じ
ちょっとこれだけだと本当にできてるか伝わりませんが、アニメーションが遷移するたびにそのアニメーションクリップ名とモーションの時間をログにはいています
キャプチャ.PNG

面倒なパターンもあるので一概にマストとは言えませんが状況に応じては設計がすっきりするかな?とも思います

間違っている点や質問などあればお願いします!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
9
Help us understand the problem. What are the problem?