Qiita Conference 2025

成瀬允宣 (@nrslib)

設計の本質:コード、システム、そして組織へ

3
2

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 5 years have passed since last update.

[Unity] 標準機能のみでボタンの連打、同時押し対応してみる

Last updated at Posted at 2019-08-14

uGUI ボタンの連打、同時押し対応は皆さんどうしているだろうか。
行き当たりばったりプロジェクトだと、その場で作るわけだが、
意外とこれで決定版というものはない気がしている。

恐らく一番多いであろうと思うのは、Button クラスを継承して、
プロジェクト独自の Button クラスを作るパターンだろうか。

しかしながら、これには一つ面倒な点が存在する。
標準 Button の Inspector は、Editor 拡張によって使いやすいよう表示されており、
自前でクラスを継承した場合、Editor 拡張の方も作らないとダメじゃんとガッカリする事になる。
各状態の色指定を設定しなければいけないなど、結構手間がかかる。
(本気でやるなら公式の公開ソースから探して、そこから作るのがよさそう。)

特にイベント周りに手を入れようとしなければ、そのままの表示でしたorz
スワイプとかに対応しようとしてカスタムボタンにあるケースが多いですね…。

また、UIデザイナーさんがUIのひな型を作ってくれる場合、ボタンの配置はやってくれることが多いため、
エンジニアの方で再度置きなおしという手間を省くことができる。

しっかり作って、代々継承していけば問題ないわけだが、
今回は新規に作る場合に、手軽に導入できて、「あ、やべ。ボタン標準のやつのままじゃん。」という時に、
簡単に導入できるものを考える。

どういうものが欲しいか

  • 連打、同時押しに対応する
  • 標準ボタンを変更しない。
  • UniRXを使わない (地味に重要)
  • 後から簡単に追加できる(既存構造に影響しない。)

こんなところだろうか。

このあたりの仕様が決まれば、自ずとやる事が見えてくる。

後から簡単に追加できる(既存構造に影響しない。)

このことから、コンポーネントで追加する形を採用する。

標準ボタンを変更しない。
連打、同時押しに対応する

標準ボタンを変更しないので、標準機能をそのまま活用する。
具体的には、 Intractable の変更で連打対応する。

同時押しに関しては、static なイベントを用意しておいて各々がそこにイベントを登録し、
イベントが発火した場合、Intractable を変更する。

ソースコード

まんまコピーし、コンポーネントとしてUIButtonSyncBlockerを追加すれば動作する。
特別なことはやっていない。ただし、要.Net4.x

UIButtonSyncBlocker
using System;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class UIButtonSyncBlocker : MonoBehaviour
{
    /// <summary>
    /// Intaractable が変更されるときに使用する Static な callback
    /// </summary>
    public static Action<bool> IntaractableSyncEvent;

    /// <summary>
    /// 個々に保持するUI
    /// </summary>
    [SerializeField] Button _SyncUIButton;

    [SerializeField] int _blockingTime = 1000;

    [SerializeField] bool _isSyncPointerUp = true;

    private bool _dontChengeFlag = false;

    private void Reset()
    {
        _SyncUIButton = GetComponent<Button>();
    }

    private void Awake()
    {
        IntaractableSyncEvent += SyncIntaractable;

        _SyncUIButton.onClick.AddListener(OnClickEvent);
    }

    private void OnDestroy()
    {
        IntaractableSyncEvent -= SyncIntaractable;
    }
    
    private void SyncIntaractable(bool isEnable)
    {
        if (isEnable)
        {
            // Enableのイベントが来た場合でも、フラグが立っている場合は元には戻さない
            if (_dontChengeFlag)
            {
                _dontChengeFlag = false;
                return;
            }
        } else
        {
            // 既に触れられない状態でコールバックが呼ばれた場合、変更対象から外す
            if (_SyncUIButton.interactable == false)
            {
                _dontChengeFlag = true;
                return;
            }
        }

        _SyncUIButton.interactable = isEnable;
    }
    
    public async void OnClickEvent()
    {
        // 同一フレームに2回イベントが発生した場合の対策
        if (_SyncUIButton.interactable == false) return;

        IntaractableSyncEvent?.Invoke(false);

        // TODO : Play SE

        await Task.Run( async () =>
        {
            await Task.Delay(_blockingTime);
        });

        IntaractableSyncEvent?.Invoke(true);
    }
}

ざっくり解説

Reset()

ランタイム時にGetComponentを避ける為、インスペクタ上見える形で、自動的にボタンを参照する。

Awake(), OnDestory()

UniRxは使わない方針のため、MonoBehaviourのイベントを使用する。
初期化時に static な領域のIntaractableSyncEventに同期処理を追加し、破壊されるときに削除する。

OnClickEvent()

既存のボタンイベントに相乗りする形で実装される。

実行順的に、先に登録されているものがある可能性があるため、
そちらからこのボタンのIntaractable を参照するようなことはしてはいけない。(当たり前)

ただのイベントの相乗りな為、どこかでonClick.RemoveAllListeners()みたいなことをすると、
当然まとめて吹き飛びます。これは運用の注意として覚えておいた方がよさそう。

Task.Delayの部分は地味にマルチスレッド。
これをCoroutineで実装してしまった場合、
このGameObjectがDisableになってしまった場合に復帰処理が停止してしまう。
こういう挙動を理解していないと酷い目に合うので、基本的ではあるが重要。

発展形

この Intaractable というやつは、Buttonクラスのプロパティではない。
継承元のUnityEngine.UI.Selectableというやつが本体である。

つまるところ、今Buttonを取っているところをSelectableとする方が美しく、
また、これに対応することにより、ToggleInputFieldなど、多くのUIを同時に制御することができる。

ただし、これらはイベントが統一されているわけではないので、そこは個々に実装する必要がある。

また、コード上に// TODO : Play SE としているところがあるが、
この機能自体はサウンドを実装する機能ではない為、任意で調整して欲しい。

発展形のものは、気が向いたらgithubなどで公開するかもしれない。
複数ファイルに跨るので、Qiita だけでの記載は避けた次第だ。

以上

3
2
0

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

Qiita Conference 2025 will be held!: 4/23(wed) - 4/25(Fri)

Qiita Conference is the largest tech conference in Qiita!

Keynote Speaker

ymrl、Masanobu Naruse, Takeshi Kano, Junichi Ito, uhyo, Hiroshi Tokumaru, MinoDriven, Minorun, Hiroyuki Sakuraba, tenntenn, drken, konifar

View event details
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?