本日は UniRx の技術調査枠です。
UniRxでObserveEveryValueChangedを使って特定の値の変化をフレームごとにチェックする方法を記事にします。
ObserveEveryValueChanged
UniRxで定義されている全クラスの拡張メソッドです。
この関数を利用することで、様々なクラスのフレーム間での値変化の監視が行えるようになります。
サンプルコード
以下のようなサンプルコードを作成しました。
2つの異なる参照変数を Subscribe することで、以下の2種類の値変化の通知を受け取ることができます
- ReactiveProperty によるベクトル情報全体の変化通知
- ObserveEveryValueChanged によるベクトルのX情報の変化通知
・EveryValueVectorReactiveProperty.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; using System; using UniRx; namespace HMProject.Test { /// <summary> /// テスト用ベクトル情報 /// </summary> [Serializable] public class EveryValueVector { public int X; public int Y; public int Z; public EveryValueVector(int x, int y, int z) { X = x; Y = y; Z = z; } } /// <summary> /// ベクトル情報を表すReactiveProperty /// </summary> [Serializable] public class EveryValueVectorReactiveProperty : ReactiveProperty<EveryValueVector> { public EveryValueVectorReactiveProperty() { } public EveryValueVectorReactiveProperty(EveryValueVector a_EveryValueVector) : base(a_EveryValueVector) { } } public class TestEveryValueChanged : MonoBehaviour { /// <summary> /// ベクトル情報 /// </summary> [SerializeField, Tooltip("ベクトル情報")] private EveryValueVectorReactiveProperty p_EveryValueVector = new EveryValueVectorReactiveProperty(); /// <summary> /// ベクトル情報のReadOnlyReactivePropertyの保持変数 /// </summary> private IReadOnlyReactiveProperty<EveryValueVector> p_IReadOnlyReactivePropertyEveryValueVector; /// <summary> /// ベクトル情報のReadOnlyReactivePropertyの参照変数 /// </summary> public IReadOnlyReactiveProperty<EveryValueVector> IReadOnlyReactivePropertyEveryValueVector => p_IReadOnlyReactivePropertyEveryValueVector ?? (p_IReadOnlyReactivePropertyEveryValueVector = p_EveryValueVector.ToSequentialReadOnlyReactiveProperty()); // EveryValueChanged を使ってベクトル情報の特定の値に変化があった際に通知する // EveryValueChanged を使うとフレーム間での変化のみを検出する /// <summary> /// ベクトル情報の EveryValueChanged オブザーバ保持変数 /// </summary> private IObservable<int> p_IObservableEveryValueVectorEveryValueChanged; /// <summary> /// ベクトル情報の EveryValueChanged オブザーバ参照変数 /// </summary> public IObservable<int> IObservableEveryValueVectorEveryValueChanged => p_IObservableEveryValueVectorEveryValueChanged ?? (p_IObservableEveryValueVectorEveryValueChanged = p_EveryValueVector.ObserveEveryValueChanged(property => property.Value.X)); // X の値の変化を検知する /// <summary> /// ベクトル情報の設定 /// </summary> private void ReceptionVector(int x, int y, int z) { p_EveryValueVector.SetValueAndForceNotify(new EveryValueVector(x, y, z)); } /// <summary> /// 起動処理 /// </summary> void Start() { // 初期値を設定する VectorReset(); } /// <summary> /// 定期処理 /// </summary> void Update() { } /// <summary> /// 初期値を設定する /// </summary> public void VectorReset() { ReceptionVector(0, 0, 0); } /// <summary> /// Xの値変更テスト /// 同一フレーム内で値を2回変更する /// </summary> public void XChangeTest() { // 値を変更する ReceptionVector(1, 0, 0); // 値を変更する ReceptionVector(2, 0, 0); } /// <summary> /// Yの値変更テスト /// 同一フレーム内で値を2回変更する /// </summary> public void YChangeTest() { // 値を変更する ReceptionVector(0, 1, 0); // 値を変更する ReceptionVector(0, 2, 0); } } #if UNITY_EDITOR // エディター定義 // 拡張するクラスを指定する [CustomEditor(typeof(TestEveryValueChanged))] // 継承クラスは Editor を設定する public class TestEveryValueChangedEditor : Editor { // GUIの表示関数をオーバーライドする public override void OnInspectorGUI() { // 元のインスペクター部分を表示 base.OnInspectorGUI(); // targetを変換して対象スクリプトの参照を取得する TestEveryValueChanged editorTarget = target as TestEveryValueChanged; // public関数を実行するボタンの作成 if (GUILayout.Button("VectorResetの実行")) { editorTarget.VectorReset(); } // public関数を実行するボタンの作成 if (GUILayout.Button("XChangeTestの実行")) { editorTarget.XChangeTest(); } // public関数を実行するボタンの作成 if (GUILayout.Button("YChangeTestの実行")) { editorTarget.YChangeTest(); } } } # endif }
合わせて参照変数に Subscribe を行い、結果を表示する以下のスクリプトを作成しました。
・TestEveryValueChangedSubscribe.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; using UniRx; namespace HMProject.Test { public class TestEveryValueChangedSubscribe : MonoBehaviour { [SerializeField, Tooltip("スクリプト参照")] private TestEveryValueChanged p_TestEveryValueChanged; /// <summary> /// 起動処理 /// </summary> void Start() { p_TestEveryValueChanged.IReadOnlyReactivePropertyEveryValueVector .ObserveOnMainThread() .Subscribe(vector => { Debug.Log("ForceNotify - X : " + vector.X.ToString() + ", Y : " + vector.Y.ToString() + ", Z : " + vector.Z.ToString()); }) .AddTo(this); p_TestEveryValueChanged.IObservableEveryValueVectorEveryValueChanged .ObserveOnMainThread() .Subscribe(value => { Debug.Log("EveryValueChanged : " + value.ToString()); }) .AddTo(this); } /// <summary> /// 定期処理 /// </summary> void Update() { } } }
作成したスクリプトを以下の通り、設定して準備は完了です。
動作確認
シーンを再生して動作を確認します。
Y の値を変化させる関数を実行すると、ReactiveProperty の通知のみが発生します。
X の値を変化させる関数を実行すると、ReactiveProperty の通知と ObserveEveryValueChanged による通知が発生します。
また、ReactiveProperty の通知は2回の変更ごとに通知が発生していますが、ObserveEveryValueChanged の通知はフレーム間の1回の変化のみを通知していることがわかります。