MRが楽しい

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

UniRxを使ってMRTKのボタンイベントをスクリプトで処理する

本日は MRTK の小ネタ枠です。
UniRxを使ってMRTKのボタンイベントをスクリプトで処理する方法について記事にします。

前提条件

MRTK を使ったプロジェクトの環境構築手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com


UniRx の環境構築手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

Interactable

MRTK のボタンコンポーネントは Interactable コンポーネントを介してレスポンスを受け取ることができます。
例えば AddListener メソッドを呼び出してスクリプトからボタンクリック時のイベントを登録することなどが可能です。
learn.microsoft.com

今回はこの AddListener を UniRx の AsObservable を使って IObservable に変換してイベントを購読できるようにしてみます。

サンプルスクリプト

以下のサンプルスクリプトを作成しました。
MRTK のトグルボタンの Interactable コンポーネントを参照し、クリックイベントを購読することでスクリプトからイベント登録と処理を行います。
・ButtonControl.cs

using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Microsoft.MixedReality.Toolkit.UI;
using UniRx;
using UnityEngine;

public class ButtonControl : MonoBehaviour
{
    [SerializeField] private Interactable mrtkButton;

    [SerializeField] private GameObject cube;

    // Start is called before the first frame update
    void Start()
    {
        mrtkButton.OnClick.AsObservable()
            .Where(_ => mrtkButton.IsToggled) // トグルの状態はクリックと同時に切り替わる
            .Subscribe(_ =>
            {
                cube.SetActive(true);
            })
            .AddTo(this);
        
        mrtkButton.OnClick.AsObservable()
            .Where(_ => !mrtkButton.IsToggled) // トグルの状態はクリックと同時に切り替わる
            .Subscribe(_ =>
            {
                cube.SetActive(false);
            })
            .AddTo(this);
    }
}

以下の通り、シーンに配置してボタンへの参照を設定しました。

シーンを再生して確認します。ボタンをクリックするとイベントが発生し、cube オブジェクトの表示が切り替わることを確認できました。


ReactivePropetyへの変換を使う

上記の例ではクリック時にのみイベントが発生するため、初期状態のイベントが発生しません。
以下のように OnClick 時にトグルの状態を参照して bool が切り替わる ReactivePropety を作成することで初期状態も検出するなども可能です。
・ButtonControl2.cs

using System;
using System.Collections;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using Microsoft.MixedReality.Toolkit.UI;
using UniRx;
using UnityEngine;

public class ButtonControl2 : MonoBehaviour
{
    [SerializeField] private Interactable mrtkButton;

    [SerializeField] private GameObject cube;

    // Start is called before the first frame update
    void Start()
    {
        var toggleReactiveProperty = mrtkButton.OnClick.AsObservable()
            .Select(_ => mrtkButton.IsToggled) // OnClick時にmrtkButton.IsToggledの値で更新を行うReactivePropertyを作成する
            .ToReactiveProperty(mrtkButton.IsToggled); // 購読時にmrtkButton.IsToggledの値を初期状態として送る

        // Toggleの変化をReactivePropertyとして受け取れる
        toggleReactiveProperty.Subscribe(cube.SetActive).AddTo(this);
    }
}