UniRxでuGUIのボタン長押しを取る

2023/02/08に公開

UniRxでuGUIのボタン長押しを取る

button.OnPointerDownAsObservable().Select(_ => true)
    .Merge(button.OnPointerUpAsObservable().Select(_ => false))
    .Throttle(TimeSpan.FromSeconds(1))
    .Where(b => b)
    .AsUnitObservable()
    .Subscribe(OnLongClick);

これで1秒の長押しが取れます。

解説

コード

UniRxって解説がないと何やってるかわかんない……! となることが多いので、一行ずつコメントを挟みます。

// OnPointerDownをtrueに変換
button.OnPointerDownAsObservable().Select(_ => true)
    // OnPointerUpをfalseに変換して合成
    // この後には押した時にtrueが、離した時にfalseが流れるようになる
    .Merge(button.OnPointerUpAsObservable().Select(_ => false))
    // 1秒間新しい値が来なかったら最後に来た値を流す
    .Throttle(TimeSpan.FromSeconds(1))
    // trueだけを流す
    .Where(b => b)
    // このboolはどうでもいい値なのでUnitにする
    .AsUnitObservable()
    // 長押しを購読!!!
    .Subscribe(OnLongClick);
いちおう書いておくとSubscribeはこんな感じ
private void OnLongClick(Unit _)
{
}

キモはThrottleです。これは指定された間新しい値が流れてこなかったら、そのときに最後に流れてきた値を流します。
今回のようにboolを流すStreamの場合は以下のような動きをするわけです。
○をtrue、✘をfalse、□を一定時間として見てください。

falseのパターン

一定時新しい値が来なかったら最後に来た値を流す、つまりこの図だと「最後にボタンを離した後、新しくボタンを押していない」ということになるのでThrottleからfalseが流れてくるわけです。

trueのパターン

この場合だと最後にtrueが流れてくる、つまり「ボタンを押した後、一定時間ボタンを離していない= 長押ししている 」ということになります。

まとめ

標準で長押し実装しろ!!!!!!!!!!!!!!! という話がまずありますね![1]
最初はMergeだけしてPointerEventDataのパラメータを見て判断しようとしていて、確かに違いはあったんですが、マルチプラットフォームで裏切ってくるタイプに見えたのでやめました。
何を意味しているのかいまいちわからないプロパティに頼るくらいなら自分で設定したboolを信じる。だいじ。

長押しって需要あると思うのですが、ネットに落ちてるやつはどれもなんか納得行かないな? みたいな感じだったので新しく考えました。自分にはわかりやすいなので満足です。

おしまい。

参考

Debounce
Gitgraph Diagrams

脚注
  1. 需要めちゃくちゃある……めちゃくちゃある! と思いましたがEventSystem周りの更新には多大な勇気がいるとかそういう話なんですかね。謎。 ↩︎

Discussion