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

UniRxでダブルクリック判定を3種類の方法で書いてみる

UnityのUniRxは便利ですし、ネットの記事を読むことで基本的な事は理解できるでしょう
しかし、いざ応用するとなるとなかなか感覚を掴めません

そこで、ダブルクリック判定は、UniRxの扱いを入門するのにちょうどいいものだと思い、
UniRxによるシングル、ダブルクリック判定法を3つ書いてみました

今回実装するダブルクリック判定の仕様は次のようです
・シングルクリックとダブルクリックをどちらも判定できる
・ダブルクリック時に、シングルクリックは判定しない
・連打時の事も考える

準備

まずはクリック判定だけ取っておきましょう

var click = this.UpdateAsObservable()
    .Where(_ => Input.GetMouseButtonDown(0));

先に言っておきます。シングルとダブルの判定は同じObservable内でbool型で判定します
シングルクリックならfalseを流し
ダブルクリックならtrueを流すことにします

1つ目

まずは、ネットでUniRx ダブルクリックなど検索するといくらか目にする例です

var dClick1 = click
    .TimeInterval() // 前のイベントとの時間差
    .Select(v => v.Interval <= TimeSpan.FromSeconds(0.2f))
        // ↑ シングルならfalse、ダブルならtrue
    .Buffer(2, 1)
    .Where(b => !(b[0] && b[1]));
        // ↑ 連打時の判定 (3連打以上は無視)

// 出力
dClick1.Subscribe(b => Debug.Log(b ? "シングルクリック" : "ダブルクリック");
    // ↑ 3項演算子によるシングル、ダブルクリック出力

クリックをした時間差で判定する方法ですね
余計に前の状態をBufferで取ってこなくてはいけませんが、UniRx初心者が理解しやすい内容と言えます。
連打時の挙動は、
最初にシングルクリック、次にダブルクリックが流れ、
後はまったく流れないです

2つ目

今度はストリームの合成の概念を使います

var dClick2 = click
    .ThrottleFirst(TimeSpan.FromSeconds(0.2f))
        // シングルクリック判定の間隔。0.2秒以内に2つ以上流さない
    .SelectMany(Observable.Return(false) // シングルクリックはfalseを流す
        .Merge(click // ダブルクリック判定につなげる
        .Select(_ => true) // ダブルクリックはtrueを流す
        .Take(TimeSpan.FromSeconds(0.2f)).Take(1)));
            // ダブルクリック判定。trueを流す

今度は、シングルクリック判定をしたら、シングルクリック判定をやめて
ダブルクリック判定を一定時間するという方法です
Returnにより、一旦シングルクリック判定を流し、
Mergeにより、ダブルクリック判定につなげます
SelectMany
 元々のObservbleにイベントが流れた時に、
 引数に渡したObservableからイベントを取り始めます

ThrottleFirstによって、シングルクリック判定を一定時間やめておき、
そのうちに、ダブルクリック判定をしています
判定時間を2回指定しなければいけないので少々気持ち悪いです

連打時の挙動は
シングルクリックとダブルクリックの判定が交互に流れます
素直な挙動です

3つ目

var dClick3 = click
    .Select(_ => false).Take(1) // シングルならfalse。一つだけ通す
    .Concat(click.Select(_ => true) // ダブルクリックならtrue
        .Take(TimeSpan.FromSeconds(0.2f)).Take(1)) // ダブルクリック判定
    .RepeatSafe(); // 判定が終わったら繰り返し

理解の難易度が高いオペレータを使いますが、一番シンプルな実装方法です
僕はこの方法が一番好きです

Concatを使っていますがこれは
 直前のObservableがOnCompletedしたら、引数に渡したObservableに切り替える
というものです
また、引数を渡さないこともでき、その場合は
 直前にSelectで指定したObservableを使う
  (この場合は一時的にIObservable<IObservable<T>>という型になる)
というようになります

シングルクリック判定が起こってから、
ダブルクリック判定をはじめて、
すべて終了すれば RepeatSafe()により最初から判定を行う
という感じです
RepeatSafe()はOnCompletedが連続で流れれば繰り返しを終了します
Repeat()なんて使うな!無限ループが怖いぞ!

連打時には
シングルクリックとダブルクリックの判定が交互に行われるので
シングルクリックとダブルクリックが交互に出力されます

出力

最後にDebug.Logの出力だけ書きます(1つ目のところでもちらっと書きました)

var doubleClick = dClick3;
doubleClick.Subscribe(b =>
    Debug.Log(b ? "ダブルクリック" : "シングルクリック");

doubleClickにdClick1や2をいれれば、そちらの方の実装を試せます

最後

ダブルクリックというのは単純なようですが、
実際に判定を取る事を考えると奥が深い処理です

この記事を書くにあたって、Concatを覚えられて良かったです

UniRxはまだまだ学習コストが高いですね

Why not register and get more from Qiita?
  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
No 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
ユーザーは見つかりませんでした