MRが楽しい

MRやVRについて学習したことを書き残す

ObservableStateMachineTriggerを使ってUniRxでAnimatorのコールバックを管理する

本日は UniRx の小ネタ枠です。
ObservableStateMachineTriggerを使ってUniRxでAnimatorのコールバックを管理する手順を記事にします。
f:id:bluebirdofoz:20210802223156j:plain

ObservableStateMachineTrigger

UniRx で提供される StateMachineBehavior のコールバックを Observable に変換する機能です。
StateMachineBehavior は Animator にアタッチすることで様々なコールバックを取得できます。
つまり、この機能を利用すると Animator のコールバックを UniRx で操作することができます。

UniRxのインポート手順

UniRxのインポート手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

Animatorを持つモデルを作成する

ObservableStateMachineTrigger の動作を確認するため、Animator コンポーネントを持つゲームオブジェクトを作成します。
今回はホロモンモデルを利用しました。
f:id:bluebirdofoz:20210802223142j:plain

Animator コンポーネントに設定する AnimatorController も作成します。
待機中のアクションと、物を探すアクションの2つのステートを遷移する AnimatorController を作成しました。
f:id:bluebirdofoz:20210802223214j:plain

シーンに配置したホロモンモデルの Animator コンポーネントに、作成した AnimatorController を設定します。
f:id:bluebirdofoz:20210802223223j:plain

StateMachineBehaviorの設定

StateMachineBehavior はデフォルトで設定されていないので、手動で追加する必要があります。
AnimatorController の画面を開き、[Add Behavior]から ObservableStateMachineTrigger を選択します。
f:id:bluebirdofoz:20210802223233j:plain

これで AnimatorController に ObservableStateMachineTrigger が追加されました。
f:id:bluebirdofoz:20210802223244j:plain

サンプルスクリプトの作成

動作確認用のサンプルスクリプトを作成します。
物を探すアクションのステートについて、State の開始をイベントトリガーとする OnStateEnterAsObservable() と、State の終了をイベントトリガーとする OnStateExitAsObservable() の動作を確認するサンプルスクリプトを作成しました。
・AnimationUniRxTest.cs

using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using UniRx;
using UniRx.Triggers;

public class AnimationUniRxTest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        // Animatorコンポーネントの参照を取得
        Animator animator = this.GetComponent<Animator>();

        // AnimatorからObservableStateMachineTriggerの参照を取得
        ObservableStateMachineTrigger trigger =
            animator.GetBehaviour<ObservableStateMachineTrigger>();

        // Stateの開始イベント
        IDisposable enterState = trigger
            .OnStateEnterAsObservable()
            .Subscribe(onStateInfo =>
            {
                AnimatorStateInfo info = onStateInfo.StateInfo;
                if(info.IsName("Base Layer.LoopAction"))
                {
                    Debug.Log("Enter to LoopAction");
                }
            }).AddTo(this);

        // Stateの終了イベント
        IDisposable exitState = trigger
            .OnStateExitAsObservable()
            .Subscribe(onStateInfo =>
            {
                AnimatorStateInfo info = onStateInfo.StateInfo;
                if (info.IsName("Base Layer.LoopAction"))
                {
                    Debug.Log("Exit from LoopAction");
                }
            }).AddTo(this);
    }
}

f:id:bluebirdofoz:20210802223257j:plain

このサンプルスクリプトを Animator コンポーネントを設定したゲームオブジェクトに追加します。
f:id:bluebirdofoz:20210802223307j:plain

動作確認

シーンを再生して動作を確認します。
f:id:bluebirdofoz:20210802223317j:plain

Parameter を変更して物を探すアクションのステートに遷移すると、物を探すステートの開始が検出されました。
f:id:bluebirdofoz:20210802223328j:plain

再度、Parameter を変更して待機中のアクションのステートに戻ると、物を探すステートの終了が検出されました。
f:id:bluebirdofoz:20210802223341j:plain

その他のトリガー

他にも以下のトリガーを設定できます。
・OnStateIKAsObservable()
・OnStateUpdateAsObservable()
・OnStateMachineEnterAsObservable()
・OnStateMachineExitAsObservable()

以下は OnStateUpdateAsObservable() を利用し、NormalizedTime の値をデバッグログに出力した例です。

        // StateのUpdateイベント
        IDisposable updateState = trigger
            .OnStateUpdateAsObservable()
            .Subscribe(onStateInfo =>
            {
                AnimatorStateInfo info = onStateInfo.StateInfo;
                if (info.IsName("Base Layer.LoopAction"))
                {
                    Debug.Log("NormalizedTime : " + info.normalizedTime.ToString());
                }
            }).AddTo(this);

f:id:bluebirdofoz:20210802223352j:plain

以下の通り、物を探すステートにとどまり続ける限り、normalizedTime の値を取得し続けます。
アクションがループしているため、1 ループを 1.0 として normalizedTime は増加し続けているのが分かります。
f:id:bluebirdofoz:20210802223403j:plain