LoginSignup
96
99

More than 5 years have passed since last update.

【Unity5】StateMachineBehaviourでAnimatorを監視する

Last updated at Posted at 2015-03-08

UniRxについての記事のまとめはこちら


StateMachineBehaviourとは

Unity5よりStateMachineBehaviourという、AnimatorControllerに貼り付けるタイプのスクリプトが作成できるようになりました。
このStateMachineBehaviourはAnimatorのステートマシンの状態遷移に合わせてCallBack関数を実行してくれるとい性質のものです。

StateMachineBehaviourのサンプル

public class StateMachineExample : StateMachineBehaviour
{

    public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        //新しいステートに移り変わった時に実行
    }

    public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        //ステートが次のステートに移り変わる直前に実行
    }

    public override void OnStateMachineEnter(Animator animator, int stateMachinePathHash)
    {
        //スクリプトが貼り付けられたステートマシンに遷移してきた時に実行
    }

    public override void OnStateMachineExit(Animator animator, int stateMachinePathHash)
    {
        //スクリプトが貼り付けられたステートマシンから出て行く時に実行
    }

    public override void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        //MonoBehaviour.OnAnimatorMoveの直後に実行される
    }

    public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        //最初と最後のフレームを除く、各フレーム単位で実行
    }

    public override void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        //MonoBehaviour.OnAnimatorIKの直後に実行される
    }
}

StateMachineBehaviourの使い方

StateMachineBehaviourは普通のComponentと違い、Animatorのレイヤに貼り付けて使います。

StateMachineBehaviour

通常のMonoBehaviourスクリプトから呼び出す場合はこんな感じで、AnimatorからGetBehaviourで取得します。

StateMachineBehaviourの取得方法
private Animator animator;
private StateMachineExample stateMachineExample;

void Start()
{
    animator = GetComponent <Animator>();
    stateMachineExample = animator.GetBehaviour <StateMachineExample>();   
}

(余談)UniRxを使いObservableとしてステート遷移を監視できるようにした

コールバックのまま使うのは少々ツライものがあるので、UniRxを使ってコールバックをObservableに変換してあげます。

StateMachineObservalbes.cs
using UnityEngine;
using System.Collections;
using UniRx;

public class StateMachineObservalbes : StateMachineBehaviour
{
    #region OnStateEnter

    private Subject<AnimatorStateInfo> onStateEnterSubject = new Subject<AnimatorStateInfo>();

    public IObservable<AnimatorStateInfo> OnStateEnterObservable { get { return onStateEnterSubject.AsObservable(); } }

    public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        onStateEnterSubject.OnNext(stateInfo);
    }

    #endregion

    #region OnStateExit

    private Subject<AnimatorStateInfo> onStateExitSubject = new Subject<AnimatorStateInfo>();

    public IObservable<AnimatorStateInfo> OnStateExitObservable { get { return onStateExitSubject.AsObservable(); } }

    public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        onStateExitSubject.OnNext(stateInfo);
    }

    #endregion

    #region OnStateMachineEnter

    private Subject<int> onStateMachineEnterSubject = new Subject<int>();

    public IObservable<int> OnStateMachineEnterObservable { get { return onStateMachineEnterSubject.AsObservable(); } }

    public override void OnStateMachineEnter(Animator animator, int stateMachinePathHash)
    {
        onStateMachineEnterSubject.OnNext(stateMachinePathHash);
    }

    #endregion

    #region OnStateMachineExit

    private Subject<int> onStateMachineExitrSubject = new Subject<int>();

    public IObservable<int> OnStateMachineExitObservable { get { return onStateMachineExitrSubject.AsObservable(); } }

    public override void OnStateMachineExit(Animator animator, int stateMachinePathHash)
    {
        onStateMachineExitrSubject.OnNext(stateMachinePathHash);
    }

    #endregion

    #region OnStateMove

    private Subject<AnimatorStateInfo> onStateMoveSubject = new Subject<AnimatorStateInfo>();

    public IObservable<AnimatorStateInfo> OnStateMoveObservable { get { return onStateMoveSubject.AsObservable(); } }

    public override void OnStateMove(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        onStateMoveSubject.OnNext(stateInfo);
    }

    #endregion

    #region OnStateMove

    private Subject<AnimatorStateInfo> onStateUpdateSubject = new Subject<AnimatorStateInfo>();

    public IObservable<AnimatorStateInfo> OnStateUpdateObservable { get { return onStateUpdateSubject.AsObservable(); } }

    public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        onStateUpdateSubject.OnNext(stateInfo);
    }

    #endregion


    #region OnStateIK

    private Subject<AnimatorStateInfo> onStateIKSubject = new Subject<AnimatorStateInfo>();

    public IObservable<AnimatorStateInfo> OnStateIKObservable { get { return onStateIKSubject.AsObservable(); } }

    public override void OnStateIK(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        onStateIKSubject.OnNext(stateInfo);
    }

    #endregion
}

あとは以下のように欲しいObservableを取得してSubscribeすれば使えます。

Observableでステート遷移を監視する
private Animator _animator;
private StateMachineObservalbes _stateMachineObservables;

void Start()
{
    _animator = GetComponent <Animator>();
    _stateMachineObservables = _animator.GetBehaviour <StateMachineObservalbes>();

    //開始したアニメーションのshortNameHashを表示する
    _stateMachineObservables
        .OnStateEnterObservable
        .Subscribe(stateInfo => Debug.Log(stateInfo.shortNameHash));
}

UniRxでステート遷移を制御してみる

「Idelアニメーションを再生し5秒以上経過したらRestモーションに遷移する」

というステート遷移を先ほどのStateMachineObservalbesを使うとこんな感じで記述できます。

private Animator _animator;
private StateMachineObservalbes _stateMachineObservables;

void Start()
{
    _animator = GetComponent <Animator>();
    _stateMachineObservables = _animator.GetBehaviour <StateMachineObservalbes>();

    _stateMachineObservables
        .OnStateEnterObservable                          //ステート遷移を監視
        .Throttle(TimeSpan.FromSeconds(5))               //最後にステート遷移してから5秒経過した時
        .Where(x => x.IsName("Base Layer.Idle"))         //現在再生中のアニメーションがBase LayerのIdelだったら
        .Subscribe(_ => _animator.SetBool("Rest", true));//AnimatorのRestパラメータをTrueにする
}

Unitychan

Idle(棒立ち)状態で5秒経ったらRest(休憩)モーションを再生するようにできました。

参考

使ったもの

ライセンス

96
99
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
96
99