Help us understand the problem. What is going on with this article?

【UniRx】マウスの長押しを判定する

More than 3 years have passed since last update.

UniRxについての記事のまとめはこちら


UniRxを使ってマウスの長押し判定を行うストリームを書いてみました。

  • 長押し判定(一定時間以上マウスがクリックされ続けた時)
  • 長押しキャンセル(一定時間以内にマウスクリックが中断された時)

をそれぞれ検出できるストリームを、オペレータチェーンのみで書くパターンとコルーチンと組み合わせるパターンで2種類書いてみました。

オペレータチェーンのみで書く場合

オペレータチェーンのみ
void Start()
{
    var mouseDownStream = this.UpdateAsObservable().Where(_ => Input.GetMouseButtonDown(0));
    var mouseUpStream = this.UpdateAsObservable().Where(_ => Input.GetMouseButtonUp(0));

    //長押しの判定
    mouseDownStream
        //マウスクリックされたら3秒後にOnNextを流す
        .SelectMany(_ => Observable.Timer(TimeSpan.FromSeconds(3)))
        //途中でMouseUpされたらストリームをリセット
        .TakeUntil(mouseUpStream)
        .RepeatUntilDestroy(this.gameObject)
        .Subscribe(_ => Debug.Log("長押し"));

    //長押しのキャンセル判定
    mouseDownStream.Timestamp()
        .Zip(mouseUpStream.Timestamp(), (d, u) => (u.Timestamp - d.Timestamp).TotalMilliseconds / 1000.0f)
        .Where(time => time < 3.0f)
        .Subscribe(t => Debug.Log(t + "秒でキャンセル"));
}

ちょっとゴリ押しですけど一応動作します。

コルーチンと組み合わせる場合

コルーチンと合わせる
public class LongClick : MonoBehaviour
{

    #region 長押しストリーム
    Subject<Unit> longClickSubject = new Subject<Unit>();

    IObservable<Unit> LongClickAsObservable
    {
        get
        {
            return longClickSubject;
        }
    }
    #endregion 

    #region 長押しキャンセルストリーム
    Subject<float> longClickCancelSubject = new Subject<float>();

    IObservable<float> LongClickCancelAsObservable
    {
        get
        {
            return longClickCancelSubject;
        }
    }

    #endregion

    Coroutine longClickCoroutine;

    void Start()
    {
        longClickCoroutine = StartCoroutine(LongClickCoroutine(3.0f, longClickSubject, longClickCancelSubject));

        LongClickAsObservable.Subscribe(_ => Debug.Log("長押し"));
        LongClickCancelAsObservable.Subscribe(t => Debug.Log(t + "秒でキャンセル"));

    }

    /// <summary>
    /// 長押し判定コルーチン
    /// </summary>
    IEnumerator LongClickCoroutine(float threshold, IObserver<Unit> longClickObserver, IObserver<float> longClickCancelObserver)
    {
        var isLongClicked = false; //長押しのOnNext発行済か
        var count = 0.0f;

        while (true)
        {
            if (Input.GetMouseButton(0))
            {
                count += Time.deltaTime;
                if (!isLongClicked && count > threshold)
                {
                    isLongClicked = true;
                    longClickObserver.OnNext(Unit.Default);
                }
            }
            else if (Input.GetMouseButtonUp(0))
            {
                isLongClicked = false;
                if (count > 0)
                {
                    longClickCancelObserver.OnNext(count);
                    count = 0;
                }
            }
            yield return null;

        }
    }


    void OnDestroy()
    {
        if (longClickCoroutine != null)
            StopCoroutine(longClickCoroutine);
    }
}

判定処理を無限ループするコルーチンの中に書き、結果をストリームで外部に通知する形にしてみました。
オペレータチェーンの連結でわけのわからないストリームになるくらいなら、諦めて手続き的に書いた方が早い場合もあります。雑多な処理は全部コルーチンに押し込まれていて外に出てこないのでまぁ許容範囲かなぁと。

またこういった汎用的に使いそうな処理はObservableTriggerとして定義しておくと良いかもしれないですね。

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away