17
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Unity/UniRx - 複数ボタンの同時押し/連打制御

Last updated at Posted at 2021-07-21

はじめに

UnityのButtonは標準で複数ボタンの同時押しや連打の制御がないため、自前で実装する必要があります。

UniRxを使うと(同じボタンの)連打に関しては記事が見つかりましたが、複数ボタンの同時押しを含めた制御について書かれている記事がなかったため、自分で実装してみました。

連打制御について

OnClickAsObservable()Buttonからクリックイベントを取得し、ThrottleFirst()を使って指定時間の間、最初の1回のイベントのみ取り出すことで連打を防ぐ制御ができます。

以下は指定時間を1秒とした場合です。

ButtonSample.cs
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
using UniRx;
using UniRx.Triggers;
using System;

public class ButtonSample : MonoBehaviour
{
    public Button Button;

    void Start()
    {
        this.Button
            .OnClickAsObservable()
            .TakeUntilDestroy(this)
            .ThrottleFirst(TimeSpan.FromMilliseconds(1000))
            .Subscribe(_ => { OnClick(); });
    }

    private void OnClick()
    {
        // ボタンが押された時の処理
    }
}

参考: UnityのButton連打を防ぎたい

同時押し/連打制御について

連打制御の方法を応用して、複数のボタンクリックイベントを合わせて最初の1回だけ取り出すようにします。

イベントを合わせるのは、Observable.Merge()を使います。

イベントを合わせた後にどのボタンから発生したイベントなのかを判別する必要があるため、Buttonから発行されたイベントをSelect()で任意のものに置き換えます。

置き換えるのは、intで0,1,2,3,...としてもいいですが、後でSwitch文で処理を条件分岐させるため、可読性の観点からenumを定義して使うのを推奨します。

ButtonSample2.cs

    // 決定ボタン
    [SerializeField] private Button ConfirmButton;
    // キャンセルボタン
    [SerializeField] private Button CancelButton;

    enum ButtonType
    {
        Confirm,
        Cancel,
    }

    private void Start()
    {
        IObservable<ButtonType> observable1 = ConfirmButton
                .OnClickAsObservable()
                .TakeUntilDestroy(this)
                .Select(_ => ButtonType.Confirm)
            ;
        
        IObservable<ButtonType> observable2 = CancelButton
                .OnClickAsObservable()
                .TakeUntilDestroy(this)
                .Select(_ => ButtonType.Cancel)
            ;
        
        Observable.Merge(observable1, observable2)
            .ThrottleFirst(TimeSpan.FromMilliseconds(1000))
            .Subscribe(x =>
            {
                switch (x)
                {
                    case ButtonType.Confirm:
                        OnClickedConfirmButton();
                        break;
                    case ButtonType.Cancel:
                        OnClickedCancelButton();
                        break;
                }
            });
    }

    private void OnClickedConfirmButton()
    {
        // 決定ボタンが押された時の処理
    }

    private void OnClickedCancelButton()
    {
        // キャンセルボタンが押された時の処理
    }

あるいは、Slect()でボタン自身を取り出してenumの定義を省く書き方もあります。

ButtonSample3.cs

    // 決定ボタン
    [SerializeField] private Button ConfirmButton;
    // キャンセルボタン
    [SerializeField] private Button CancelButton;

    private void Start()
    {
        IObservable<ButtonType> observable1 = ConfirmButton
                .OnClickAsObservable()
                .TakeUntilDestroy(this)
                .Select(_ => ConfirmButton)
            ;
        
        IObservable<ButtonType> observable2 = CancelButton
                .OnClickAsObservable()
                .TakeUntilDestroy(this)
                .Select(_ => CancelButton)
            ;
        
        Observable.Merge(observable1, observable2)
            .ThrottleFirst(TimeSpan.FromMilliseconds(1000))
            .Subscribe(x =>
            {
                if (x == ConfirmButton)
                {
                    OnClickedConfirmButton();
                }
                else if (x == CancelButton)
                {
                    OnClickedCancelButton();
                }
            });
    }

    private void OnClickedConfirmButton()
    {
        // 決定ボタンが押された時の処理
    }

    private void OnClickedCancelButton()
    {
        // キャンセルボタンが押された時の処理
    }

あとがき

UniRxを使わない場合、boolを定義してフラグ管理を駆使する方法が考えられます。

しかし、この方法ではフラグの初期化を忘れてボタンを押せなくなる...みたいなバグが発生しがちで煩雑です。

UniRxのThrottleFirst()を使うことでこの煩わしさから解放されるメリットは大きいと思います。

ボタンの数が片手で数えられるくらいならこの書き方でまかなえると思いますが、ScrollViewにセルがたくさんあって画面全体で制御をしないといけない場合はもっと捻った解決策が必要な想定です。

17
14
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?