本ページにはプロモーションが含まれます
PlaymakerUnity

【Unity】PlaymakerでUIボタンの連打防止方法

この記事は約5分で読めます。

はじめに

UIボタンのClickリスナーからPlaymakerのFSMに、SendEvent を呼び出す処理は、Playmakerを愛用するクリエイターの皆さんにとってはおなじみの方法かと思います。しかし、FSM側で連打防止の対策をしないと、クリックのたびにイベントが呼び出されてしまいます。

たとえば、何かアクションが実行されたらボタンを非活性にするというのはよくあるパターンですが、熱心に連打するユーザーはそれでも止まりません。非同期処理とか実装されているといつの間にかリクエストが通っているなんてざらです。そこで、Playmakerで扱いやすいよう連打防止スクリプトの仕組みを作ってみました。

スクリプトを使ったUIボタン連打防止処理

using UnityEngine;
using UnityEngine.UI;

public class ButtonSpamPrevention : MonoBehaviour
{
    public Button myButton; // 連打を防止したいボタン
    private bool isButtonCoolingDown = false; // ボタンがクールダウン中かどうかを追跡するフラグ
    public float cooldownTime = 1.0f; // ボタンが再度押せるようになるまでのクールダウン時間(秒)

    void Start()
    {
        myButton.onClick.AddListener(OnButtonClick); // ボタンのクリックイベントにリスナーを追加
    }

    void OnButtonClick()
    {
        if (!isButtonCoolingDown)
        {

            Debug.Log("ボタンの機能をここに実装");

            // クールダウン開始
            isButtonCoolingDown = true;
            Invoke("ResetCooldown", cooldownTime);
        }
    }

    void ResetCooldown()
    {
        // クールダウン終了
        isButtonCoolingDown = false;
    }
}

このスクリプトをGameManager的なGameObjectに貼り付けて、対応したいボタンをアサインして使用します。ボタンがたくさんある場合は、スクリプト改造して複数ボタンをアサインするようにすれば同じ効果を出すことができるかと想います。このコードにボタンのenableとか実装してあげれば、ボタン押下→ボタン非アクティブ→x秒後にボタンアクティブみたいなこともできるかと思います。

UIボタン連打防止処理をカスタムアクション化

上記スクリプトを応用してPlaymakerのカスタムアクションにしてみました。

using HutongGames.PlayMaker;
using UnityEngine;

[ActionCategory("General")]
public class PreventButtonSpam : FsmStateAction
{
    [RequiredField]
    public FsmOwnerDefault buttonGameObject;

    [RequiredField]
    public FsmFloat cooldownTime;

    [RequiredField]
    public FsmBool isCooldown;

    public FsmEvent buttonPressedEvent;

    private float cooldownTimer;

    public override void Reset()
    {
        buttonGameObject = null;
        isCooldown = null;
        cooldownTime = null;
        buttonPressedEvent = null;
    }

    public override void OnEnter()
    {
        cooldownTimer = 0f;

        var go = Fsm.GetOwnerDefaultTarget(buttonGameObject);
        if (go != null)
        {
            OnButtonClick();
        }
    }

    public override void OnUpdate()
    {
        if (isCooldown.Value)
        {

            cooldownTimer += Time.deltaTime;

            if (cooldownTimer >= cooldownTime.Value)
            {
                isCooldown.Value = false;
                cooldownTimer = 0f;
            }
        }
    }

    public void OnButtonClick()
    {
        if (!isCooldown.Value)
        {
            isCooldown.Value = true;
            cooldownTimer = 0f;
            Fsm.Event(buttonPressedEvent);
        }
    }
}

ご自由にご利用ください。

ClickリスナーからボタンにアタッチしてあるFSMのSendEventでボタン呼び出す。

アクションごとに色付けができたらいいなーと思っている。

実行され3秒経過しないと、buttonPressedEventは再発火されない設定。
isCooldownのbool値をFSMのローカル変数を使うことで一時的に状態を保持するフラグになる。
再発火不要で使いまわしたい場合は、前提として、該当のFSMがアクティブ、非アクティブになるのであれば
48行目あたりをコメントアウトして、FSMのStartでisCooldownをFalseにする処理入れればアクティブ毎にワンタイム発火になる。

おわりに

思ったより使い勝手よかった。※あくまで私の実装物に対して相性が良いという意味で。