MRが楽しい

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

MRTKの手の検出イベントをUniRxのオブザーバで処理する

本日は MRTK と UniRx の小ネタ枠です。
MRTKの手の検出イベントをUniRxのオブザーバで処理する方法を記事にします。
f:id:bluebirdofoz:20210416085616j:plain

FromEvent

FromEvent はイベントから Observable を作成する UniRx のファクトリメソッドです。
以下のように Action クラスから Observable を作成することができます。

Action OnEvent;

IObservable OnEventObservable = Observable.FromEvent(
    action => OnEvent += action,
    action => OnEvent -= action
    );

引数のあるイベントの場合は以下のように作成します。

Action<int> OnEvent;

IObservable<int> OnEventObservable = Observable.FromEvent<int>(
    action => OnEvent += action,
    action => OnEvent -= action
    );

IMixedRealitySourceStateHandlerとIMixedRealityHandJointHandler

IMixedRealitySourceStateHandler と IMixedRealityHandJointHandler は手の検出時にイベントを発生させるハンドラです。
OnSourceDetected と OnSourceLost で手の検出とロストをイベントとして取得できます。
OnHandJointsUpdated で詳細な手の関節(ハンドジョイント)情報を取得できます。
docs.microsoft.com

サンプルコード

以下のようなサンプルコードを作成しました。
3つの手の検出イベントそれぞれのオブザーバを作成し、情報をログに出力しています。
OnHandJointsUpdated のみ常に Handedness.None イベントが発生するため、Where オペレータで除外しています。
・TestFromEvent.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System;

using Microsoft.MixedReality.Toolkit;
using Microsoft.MixedReality.Toolkit.Input;
using Microsoft.MixedReality.Toolkit.Utilities;

using UniRx;

namespace HMProject.Test
{
    public class TestFromEvent : MonoBehaviour, IMixedRealitySourceStateHandler, IMixedRealityHandJointHandler
    {
        // 手の検出時に呼び出すアクション
        private Action<SourceStateEventData> OnSourceDetectedAction;

        // 手のロスト時に呼び出すアクション
        private Action<SourceStateEventData> OnSourceLostAction;

        // 手の更新時に呼び出すアクション
        private Action<InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>>> OnHandJointsUpdatedAction;


        /// <summary>
        /// 手の検出時に発生するイベント(IMixedRealitySourceStateHandler)
        /// </summary>
        /// <param name="eventData"></param>
        public void OnSourceDetected(SourceStateEventData eventData)
        {
            // アクション呼び出し
            OnSourceDetectedAction(eventData);
        }

        /// <summary>
        /// 手のロスト時に発生するイベント(IMixedRealitySourceStateHandler)
        /// </summary>
        /// <param name="eventData"></param>
        public void OnSourceLost(SourceStateEventData eventData)
        {
            // アクション呼び出し
            OnSourceLostAction(eventData);
        }

        /// <summary>
        /// 手の更新時に発生するイベント(IMixedRealityHandJointHandler)
        /// </summary>
        /// <param name="eventData"></param>
        public void OnHandJointsUpdated(InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>> eventData)

        {
            // アクション呼び出し
            OnHandJointsUpdatedAction(eventData);
        }


        /// <summary>
        /// 起動処理
        /// </summary>
        void Start()
        {
            // 手の検出時オブザーバを作成する
            IDisposable OnSourceDetectedObserver = Observable
                .FromEvent<SourceStateEventData>(
                    action => OnSourceDetectedAction += action,
                    action => OnSourceDetectedAction -= action
                )
                .Subscribe(eventData =>
                {
                    Debug.Log("!!! OnSourceDetected !!! - SourceId : " + eventData.SourceId.ToString());
                })
                .AddTo(this);

            // 手のロスト時オブザーバを作成する
            IDisposable OnSourceLostObserver = Observable
                .FromEvent<SourceStateEventData>(
                    action => OnSourceLostAction += action,
                    action => OnSourceLostAction -= action
                )
                .Subscribe(eventData =>
                {
                    Debug.Log("!!! OnSourceLost !!! - SourceId : " + eventData.SourceId.ToString());
                })
                .AddTo(this);

            // 手の更新時オブザーバを作成する
            IDisposable OnHandJointsUpdatedObserver = Observable
                .FromEvent<InputEventData<IDictionary<TrackedHandJoint, MixedRealityPose>>>(
                    action => OnHandJointsUpdatedAction += action,
                    action => OnHandJointsUpdatedAction -= action
                )
                .Where(eventData => eventData.Handedness != Handedness.None)   // Noneイベントは処理しない
                .Subscribe(eventData =>
                {
                    Debug.Log("!!! OnHandJointsUpdated !!! " +
                        "- SourceId : " + eventData.SourceId.ToString() +
                        ", Handedness : " + eventData.Handedness.ToString());
                })
                .AddTo(this);

        }


        /// <summary>
        /// 有効時処理
        /// </summary>
        private void OnEnable()
        {
            // ハンドラ登録
            CoreServices.InputSystem?.RegisterHandler<IMixedRealityHandJointHandler>(this);
            CoreServices.InputSystem?.RegisterHandler<IMixedRealitySourceStateHandler>(this);
        }

        /// <summary>
        /// 無効時処理
        /// </summary>
        private void OnDisable()
        {
            // ハンドラ解除
            CoreServices.InputSystem?.UnregisterHandler<IMixedRealityHandJointHandler>(this);
            CoreServices.InputSystem?.UnregisterHandler<IMixedRealitySourceStateHandler>(this);
        }
    }
}

f:id:bluebirdofoz:20210416090005j:plain

スクリプトを適当なオブジェクトに追加します。
f:id:bluebirdofoz:20210416090020j:plain

動作確認

シーンを再生して動作を確認します。
ログを確認すると、手の検出イベントをオブザーバが受け取っていることが分かります。
これで MRTK で発生するイベントを UniRx の仕組みを使ってフィルタリングすることができます。
f:id:bluebirdofoz:20210416090035j:plain