UniRxについての記事のまとめはこちら
StateMachineBehaviourとは
Unity5よりStateMachineBehaviourという、AnimatorControllerに貼り付けるタイプのスクリプトが作成できるようになりました。
このStateMachineBehaviourはAnimatorのステートマシンの状態遷移に合わせてCallBack関数を実行してくれるとい性質のものです。
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のレイヤに貼り付けて使います。
通常のMonoBehaviourスクリプトから呼び出す場合はこんな感じで、AnimatorからGetBehaviourで取得します。
private Animator animator;
private StateMachineExample stateMachineExample;
void Start()
{
animator = GetComponent <Animator>();
stateMachineExample = animator.GetBehaviour <StateMachineExample>();
}
#(余談)UniRxを使いObservableとしてステート遷移を監視できるようにした
コールバックのまま使うのは少々ツライものがあるので、UniRxを使ってコールバックをObservableに変換してあげます。
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すれば使えます。
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にする
}
Idle(棒立ち)状態で5秒経ったらRest(休憩)モーションを再生するようにできました。
##参考
##使ったもの
- [UniRx] (https://github.com/neuecc/UniRx)
- ユニティちゃん