本日は UniRx の小ネタ枠です。
UniRxのReactivePropertyをToSequentialReadOnlyReactivePropertyで参照して強制通知を有効にする方法を記事にします。
前提条件
前回記事の続きです。
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); } // ---------- 中略 ---------- } }
上記のスクリプトの参照用の変数を以下のスクリプトで購読して、変更の通知を受け取ります。
・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; } } // ---------- 中略 ---------- } }
こちらのコードで動作確認を行ってみます。
同じ言葉が連続した場合に、SetValueAndForceNotify を使って値を設定しているにも関わらず、2回目の通知が発生しない問題が起こります。
対処方法
これは 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); } // ---------- 中略 ---------- }
このコードで改めて動作確認を行ってみます。
こちらだと同じ言葉が連続した場合にも、強制的に通知を発生させることができました。