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はまだまだ学習コストが高いですね