MRが楽しい

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

UniRxのReactivePropertyをToSequentialReadOnlyReactivePropertyで参照して強制通知を有効にする

本日は UniRx の小ネタ枠です。
UniRxのReactivePropertyをToSequentialReadOnlyReactivePropertyで参照して強制通知を有効にする方法を記事にします。
f:id:bluebirdofoz:20210307114947j:plain

前提条件

前回記事の続きです。
bluebirdofoz.hatenablog.com

強制通知が飛ばない問題

ReactiveProperty を前回記事で紹介した ToReadOnlyReactiveProperty() で参照した場合、SetValueAndForceNotify を使った同じ値での変更通知が受け取れない問題が発生します。

例えば、以下のような音声認識を通知するクラスを作成してみました。
ToReadOnlyReactiveProperty() を使って参照用の変数を生成しています。
・HoloMonListenSingleton.cs

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

namespace HMProject.HoloMon
{
    public class HoloMonListenSingleton : MonoBehaviour
    {

        // ---------- 中略 ----------

        /// <summary>
        /// ホロモンが認識した言葉(最新)
        /// </summary>
        [SerializeField, Tooltip("ホロモンが認識した言葉(最新)")]
        private HoloMonListenReactiveProperty p_HoloMonListenWord
            = new HoloMonListenReactiveProperty(HoloMonListenWord.Nothing);

        /// <summary>
        /// ホロモンが認識した言葉(最新)のReadOnlyReactivePropertyの保持変数
        /// </summary>
        private IReadOnlyReactiveProperty<HoloMonListenWord> p_IReadOnlyReactivePropertyHoloMonListenWord;

        /// <summary>
        /// ホロモンが認識した言葉(最新)のReadOnlyReactivePropertyの参照変数
        /// </summary>
        public IReadOnlyReactiveProperty<HoloMonListenWord> IReadOnlyReactivePropertyHoloMonListenWord
            => p_IReadOnlyReactivePropertyHoloMonListenWord
            // ToReadOnlyReactiveProperty() を使って参照用の変数を生成する
            ?? (p_IReadOnlyReactivePropertyHoloMonListenWord = p_HoloMonListenWord.ToReadOnlyReactiveProperty());

        /// <summary>
        /// ホロモンが今まで認識した言葉のリスト
        /// </summary>
        [SerializeField, Tooltip("ホロモンが今まで認識した言葉のリスト")]
        private List<HoloMonListenWord> p_UntilNowListenWords = new List<HoloMonListenWord>();

        /// <summary>
        /// 言葉の受付
        /// </summary>
        /// <param name="a_ListenWord"></param>
        private void ReceptionListenWord(HoloMonListenWord a_ListenWord)
        {
            // 変数の登録とリストへの追加を行う
            // 連続して同じ言葉の設定を行う場合も SetValueAndForceNotify で強制的に通知を飛ばす
            p_HoloMonListenWord.SetValueAndForceNotify(a_ListenWord);
            p_UntilNowListenWords.Add(a_ListenWord);
            if (p_UntilNowListenWords.Count > 10) p_UntilNowListenWords.RemoveAt(0);
        }

        // ---------- 中略 ----------

    }
}

f:id:bluebirdofoz:20210307115059j:plain

上記のスクリプトの参照用の変数を以下のスクリプトで購読して、変更の通知を受け取ります。
・HoloMonModeLogicRockPaperScissors.cs

using UnityEngine;
using System;
using UniRx;

using HMProject.HoloMon;
using HMProject.HoloMonRockPaperScissors;

namespace HMProject.HoloMonLogic
{
    public class HoloMonModeLogicRockPaperScissors : MonoBehaviour, HoloMonLogicInterface
    {

        // ---------- 中略 ----------

        /// <summary>
        /// 開始処理
        /// </summary>
        void Start()
        {
            // 音声聞き取り時の処理を設定する
            HoloMonListenSingleton.Instance.IReadOnlyReactivePropertyHoloMonListenWord
                .ObserveOnMainThread()
                .Subscribe(word => {
                    ListenWord(word);
                })
                .AddTo(this);
        }

        /// <summary>
        /// 聞き取った言葉に合わせたアクションを実行する
        /// </summary>
        /// <param name="a_ListenWord"></param>
        private void ListenWord(HoloMonListenWord a_ListenWord)
        {
            Debug.Log("ListenWord : " + a_ListenWord.ToString());
            switch (a_ListenWord)
            {
                case HoloMonListenWord.PonSyo:
                    // ぽん or あいこでしょという言葉を聞いた場合
                    CallReactionPonSyo();
                    break;
                default:
                    break;
            }
        }

        // ---------- 中略 ----------

    }
}

f:id:bluebirdofoz:20210307115016j:plain

こちらのコードで動作確認を行ってみます。
同じ言葉が連続した場合に、SetValueAndForceNotify を使って値を設定しているにも関わらず、2回目の通知が発生しない問題が起こります。
f:id:bluebirdofoz:20210307115028j:plain

対処方法

これは ToReadOnlyReactiveProperty() で作成した ReadOnlyReactiveProperty 側でも、同じ値の変更がフィルタされてしまうことが原因です。
フィルタを行わない ReadOnlyReactiveProperty を生成するには ToSequentialReadOnlyReactiveProperty() を利用します。

以下のように参照用の変数を作成するコードを、ToSequentialReadOnlyReactiveProperty() を使って修正してみます。
・HoloMonListenSingleton.cs

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

namespace HMProject.HoloMon
{
    public class HoloMonListenSingleton : MonoBehaviour
    {

        // ---------- 中略 ----------

        /// <summary>
        /// ホロモンが認識した言葉(最新)
        /// </summary>
        [SerializeField, Tooltip("ホロモンが認識した言葉(最新)")]
        private HoloMonListenReactiveProperty p_HoloMonListenWord
            = new HoloMonListenReactiveProperty(HoloMonListenWord.Nothing);

        /// <summary>
        /// ホロモンが認識した言葉(最新)のReadOnlyReactivePropertyの保持変数
        /// </summary>
        private IReadOnlyReactiveProperty<HoloMonListenWord> p_IReadOnlyReactivePropertyHoloMonListenWord;

        /// <summary>
        /// ホロモンが認識した言葉(最新)のReadOnlyReactivePropertyの参照変数
        /// </summary>
        public IReadOnlyReactiveProperty<HoloMonListenWord> IReadOnlyReactivePropertyHoloMonListenWord
            => p_IReadOnlyReactivePropertyHoloMonListenWord
            // ---------- 変更箇所 ここから ----------
            // ToSequentialReadOnlyReactiveProperty() で参照用の変数を生成する
            ?? (p_IReadOnlyReactivePropertyHoloMonListenWord = p_HoloMonListenWord.ToSequentialReadOnlyReactiveProperty());
            // ---------- 変更箇所 ここまで ----------

        /// <summary>
        /// ホロモンが今まで認識した言葉のリスト
        /// </summary>
        [SerializeField, Tooltip("ホロモンが今まで認識した言葉のリスト")]
        private List<HoloMonListenWord> p_UntilNowListenWords = new List<HoloMonListenWord>();

        /// <summary>
        /// 言葉の受付
        /// </summary>
        /// <param name="a_ListenWord"></param>
        private void ReceptionListenWord(HoloMonListenWord a_ListenWord)
        {
            // 変数の登録とリストへの追加を行う
            // 連続して同じ言葉の設定を行う場合も SetValueAndForceNotify で強制的に通知を飛ばす
            p_HoloMonListenWord.SetValueAndForceNotify(a_ListenWord);
            p_UntilNowListenWords.Add(a_ListenWord);
            if (p_UntilNowListenWords.Count > 10) p_UntilNowListenWords.RemoveAt(0);
        }

        // ---------- 中略 ----------

}

f:id:bluebirdofoz:20210307115138j:plain

このコードで改めて動作確認を行ってみます。
こちらだと同じ言葉が連続した場合にも、強制的に通知を発生させることができました。
f:id:bluebirdofoz:20210307115149j:plain