i-school - UniRx を活用したマウス長押し機能の実装例
 UniRx を活用したマウス長押し機能の実装例です。



サンプルコード



using System;
using UniRx;
using UniRx.Triggers;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class Variable : MonoBehaviour
{
    [SerializeField, Tooltip("計算機に変数追加ボタン")] private Button btnVariable;

    [SerializeField, Tooltip("変数名表示テキスト")] private Text textVariableName;
    [SerializeField, Tooltip("変数の値表示テキスト")] private Text textVriableValue;

    public ReactiveProperty<string> variableName { get; set; } = new();
    public ReactiveProperty<int> VariableValue { get; set; } = new();


    void Start() {
    // デバッグ用
        GenerateSetup("Sample", 0);
    }

    public void GenerateSetup(string viewText, int index, UnityAction<int> buttonActionAddCalcView = null)
    {
        btnVariable.OnClickAsObservable()
            .ThrottleFirst(System.TimeSpan.FromSeconds(0.5f))
            .Subscribe(_ =>
            {
                buttonActionAddCalcView?.Invoke(index);
            })
            .AddTo(this);

    // UniRx を利用したマウスの長押し処理
    // OnPointerDownをtrueに変換
        btnVariable.OnPointerDownAsObservable().Select(_ => true)
       
         // OnPointerUpをfalseに変換
           // OnPointerDownとOnPointerUpを1つのストリームに合成
           // この後には押した時にtrueが、離した時にfalseが流れるようになる
         .Merge(btnVariable.OnPointerUpAsObservable().Select(_ => false))

           // 1秒間新しい値が来なかったら最後に来た値を流す
           .Throttle(TimeSpan.FromSeconds(0.5f))

           // trueだけを流す
           .Where(b => b)

           // このboolは利用しない値なので、通知発行のみを行っていることを明確にするため、Unitに変換する
         .AsUnitObservable()

         // 上記の処理により、最終的にマウスの長押しを購読する
         .Subscribe(_ =>
         {
             // TODO 長押し時の処理を実装

         })
         .AddTo(this);

        variableName
            .Subscribe(message =>
            {
                SetVariableName(message);
            })
            .AddTo(this);

        variableName.Value = viewText;

        VariableValue.Subscribe(x =>
            {
                SetVariableValue(x.ToString());
            })
            .AddTo(this);

        VariableValue.Value = 0;
    }

    public void SetVariableName(string value)
    {
        textVariableName.text = value;
    }

    public void SetVariableValue(string value)
    {
        textVriableValue.text = value;
    }
}


解説


 btnVariableのOnPointerDownとOnPointerUpを1つのストリームに合成して、
その後にTrueのときだけをフィルタリングし、最終的にUnitに変換してから購読しています。

    // UniRx を利用したマウスの長押し処理
    // OnPointerDownをtrueに変換
        btnVariable.OnPointerDownAsObservable().Select(_ => true)
       
         // OnPointerUpをfalseに変換
           // OnPointerDownとOnPointerUpを1つのストリームに合成
           // この後には押した時にtrueが、離した時にfalseが流れるようになる
         .Merge(btnVariable.OnPointerUpAsObservable().Select(_ => false))

           // 1秒間新しい値が来なかったら最後に来た値を流す
           .Throttle(TimeSpan.FromSeconds(0.5f))

           // trueだけを流す
           .Where(b => b)

           // このboolは利用しない値なので、通知発行のみを行っていることを明確にするため、Unitに変換する
         .AsUnitObservable()

         // 上記の処理により、最終的にマウスの長押しを購読する
         .Subscribe(_ =>
         {
             // TODO 長押し時の処理を実装

         })
         .AddTo(this);

 具体的な処理の流れは以下の通りです。

 OnPointerDownAsObservable()とOnPointerUpAsObservable()をMergeして1つのストリームに結合します。

 合成したストリームの各要素に対して、Select(_ => true)とSelect(_ => false)を使って、それぞれTrueとFalseに変換します。

 変換したストリームをThrottleで0.5秒ごとに制御します。これにより、長押し中でも0.5秒ごとに1回だけイベントが流れます。

 最後に、Trueのときだけをフィルタリングして、AsUnitObservable()でUnitに変換します。

 最終的に、長押し時には1秒ごとにUnitが流れるようになり、そのイベントをSubscribeして実装した処理を呼び出します。
この処理により、btnVariableが長押し(またはタップ)された状態を検知し、その際にイベント処理を呼び出すことができます。


<AsUnitObservable() オペレータ>


 AsUnitObservable() オペレータは、Observableの各要素に対して単なる true ではなく、 Unit を流すように変換するものです。
これは、Observableが特定の値の流れではなく、単なるイベントの発生を表す場合に使われます。

 例えば、 BtnVariable が長押し(またはタップ)されたことを知りたい場合、その情報だけを持つ true を流すのではなく、
Unit を流すことで、特に値に意味がなく、単にイベントが発生したことを表現することができます。

 実際には、この場合、 AsUnitObservable() を挟まなくても動作することがあります。
しかし、イベントの発生を表す場面では、 Unit が使用されることが一般的であり、コードの意図を明示的にするために使われることがあります。
これにより、コードを見る人が単なる true の代わりに Unit を使っていることを理解しやすくなります。

 また、特に値に意味がない場合や、その値を無視する場合にも Unit が使われることがあります。
したがって、このような文脈で .AsUnitObservable() を利用することで、コードの意図が明確になり、他の開発者や自分自身が後で理解しやすくなります。