RのWeb制作

Webサービス制作のための技術情報を。データ分析(Python、機械学習コンペ他)や自作野球ゲームMeisyoのこと中心。

Meisyo MeisyoNX

[Unity]かんたんに編集・管理ができるボタン向けカスタムクラスのコード紹介

投稿日:

「ボタンが増えて、変更したり管理がめんどくさい・・・!」そんなあなたに。
Unity向け、編集が容易なボタンのカスタムクラスのコードを紹介します。

特徴

・クリック・長押し・スワイプ判定対応のクラスです。
・CustomButtonUI.csを変更することで、同じスクリプトをアタッチしているオブジェクトのクリック時の挙動やクリック音などを一括で管理・変更可能です。
・複数クラスを作ることで、「別の挙動で統一したい!」「別の音で統一したい!」ということもできます。

使い方

①対象のオブジェクトの親にからオブジェクトを作成し、そこにアタッチして利用してください。

例:Imageオブジェクトをクリック判定させたい場合
(1)ImageGoldオブジェクト(空オブジェクトを)作り、そこにスクリプトをアタッチします。
(2)Canvas RendererとCanvas Groupを追加でアタッチします。
(3)アタッチしたスクリプトのCanvas Group変数に、自身のCanvas Groupを選択(またはドロップダウンで指定)します。

※コピペすると、そのオブジェクトも全く同じ挙動をします。
※CustomButtonUI.csを変更すると、元のオブジェクトもコピペしたオブジェクトも変更後の挙動をします。

②クリックイベントを実装してください。
アタッチしたオブジェクトに対し、クリックイベントを入れてください。

// 定義
[SerializeField] GameObject objBtnPractice;

// どこぞに入れて...
objBtnPractice.GetComponent<CustomButtonUI>().onClickCallback = () => {
    // ここに実行したい関数を入れる
};

コード本体

using DG.Tweening;
using UnityEngine;
using UnityEngine.EventSystems;

// スワイプの方向を示すenum
public enum SwipeDirection
{
    None,
    Up,
    Down,
    Left,
    Right
}

// ボタン用クラス
public class CustomButtonUI : MonoBehaviour,
    IPointerClickHandler,
    IPointerDownHandler,
    IPointerUpHandler
{
    //------------------------------------------------------//
    // 設定
    //------------------------------------------------------//
    // 一般
    [SerializeField] private CanvasGroup buttonCanvasGroup;

    // クリック判定
    public System.Action onClickCallback;

    // 長押し判定
    public System.Action onPressLongCallback;
    [SerializeField] private float longPressDuration = 0.5f;
    private float longPressStartTime = 0f;
    private bool isPointerDown = false;

    // スワイプ判定
    public System.Action<SwipeDirection> onSwipeCallback;
    private Vector2 swipeStartPosition;
    private Vector2 swipeCurrentPosition;
    [SerializeField] private bool detectSwipeOnlyAfterRelease = false;
    [SerializeField] private float swipeThreshold = 20f;

    //------------------------------------------------------//
    // 関数:一般
    //------------------------------------------------------//
    private void Update()
    {
        // 長押し
        if(isPointerDown == true)
        {
            if(Time.time - longPressStartTime > longPressDuration)
            {
                OnPressLong();
                ResetPressLong();
            }
        }

        // スワイプ
        if (Input.touchCount > 0) // タッチ入力(モバイル)
        {
            foreach (Touch touch in Input.touches)
            {
                if (touch.phase == TouchPhase.Began)
                {
                    swipeCurrentPosition = touch.position;
                    swipeStartPosition = touch.position;
                }

                if (touch.phase == TouchPhase.Moved)
                {
                    if (!detectSwipeOnlyAfterRelease)
                    {
                        swipeCurrentPosition = touch.position;
                        CheckSwipe();
                    }
                }

                if (touch.phase == TouchPhase.Ended)
                {
                    swipeCurrentPosition = touch.position;
                    CheckSwipe();
                }
            }
        }
        // マウス入力(Unity Editor用)
        else
        {
            if (Input.GetMouseButtonDown(0))
            {
                swipeCurrentPosition = Input.mousePosition;
                swipeStartPosition = Input.mousePosition;
            }

            if (Input.GetMouseButton(0))
            {
                if (!detectSwipeOnlyAfterRelease)
                {
                    swipeCurrentPosition = Input.mousePosition;
                    CheckSwipe();
                }
            }

            if (Input.GetMouseButtonUp(0))
            {
                swipeCurrentPosition = Input.mousePosition;
                CheckSwipe();
            }
        }
    }

    public void OnPointerDown(PointerEventData eventData)
    {
        transform.DOScale(0.95f, 0.24f).SetEase(Ease.OutCubic);
        buttonCanvasGroup.DOFade(0.8f, 0.24f).SetEase(Ease.OutCubic);

        // カウント開始
        isPointerDown = true;
        longPressStartTime = Time.time;

        // スワイプ開始位置記録
        swipeStartPosition = eventData.position;
        swipeCurrentPosition = eventData.position;
    }

    public void OnPointerUp(PointerEventData eventData)
    {
        transform.DOScale(1f, 0.24f).SetEase(Ease.OutCubic);
        buttonCanvasGroup.DOFade(1f, 0.24f).SetEase(Ease.OutCubic);

        // スワイプ終了位置記録
        swipeCurrentPosition = eventData.position;
        CheckSwipe();

        // リセット
        ResetPressLong();
    }

    //------------------------------------------------------//
    // 関数:クリック
    //------------------------------------------------------//
    public void OnPointerClick(PointerEventData eventData)
    {
        if(onClickCallback != null)
        {
            // サウンド
            // SoundManager.instance.PlaySE(SoundDataSE.Type.ClickUI);

            // invoke
            onClickCallback.Invoke();
        }
        Logging.Log($"[{name}] クリック");
    }

    //------------------------------------------------------//
    // 関数:長押し
    //------------------------------------------------------//
    public void OnPressLong()
    {
        if(onPressLongCallback != null)
        {
            // サウンド
            // SoundManager.instance.PlaySE(SoundDataSE.Type.ClickLong);

            // invoke
            onPressLongCallback.Invoke();
        }
        Logging.Log($"[{name}] 長押し");
    }

    // 状態をリセット
    private void ResetPressLong()
    {
        isPointerDown = false;
        longPressStartTime = 0f;
    }

    //------------------------------------------------------//
    // 関数:スワイプ
    //------------------------------------------------------//
    // スワイプ判定
    private void CheckSwipe()
    {
        SwipeDirection direction = SwipeDirection.None;

        // しきい値以上縦方向にスワイプしたかどうか判定する
        if (GetVerticalDistance() > swipeThreshold && GetVerticalDistance() > GetHorizontalDistance())
        {
            if (swipeCurrentPosition.y - swipeStartPosition.y > 0)
            {
                direction = SwipeDirection.Up;
            }
            else if (swipeCurrentPosition.y - swipeStartPosition.y < 0)
            {
                direction = SwipeDirection.Down;
            }
            swipeStartPosition = swipeCurrentPosition;
        }
        // しきい値以上横方向にスワイプしたかどうか判定する
        else if (GetHorizontalDistance() > swipeThreshold && GetHorizontalDistance() > GetVerticalDistance())
        {
            if (swipeCurrentPosition.x - swipeStartPosition.x > 0)
            {
                direction = SwipeDirection.Right;
            }
            else if (swipeCurrentPosition.x - swipeStartPosition.x < 0)
            {
                direction = SwipeDirection.Left;
            }
            swipeStartPosition = swipeCurrentPosition;
        }

        // スワイプ検出時にコールバック実行
        if (direction != SwipeDirection.None)
        {
            OnSwipe(direction);
        }
    }

    private float GetVerticalDistance()
    {
        return Mathf.Abs(swipeCurrentPosition.y - swipeStartPosition.y);
    }

    private float GetHorizontalDistance()
    {
        return Mathf.Abs(swipeCurrentPosition.x - swipeStartPosition.x);
    }

    private void OnSwipe(SwipeDirection direction)
    {
        if(onSwipeCallback != null)
        {
            // サウンド
            // SoundManager.instance.PlaySE(SoundDataSE.Type.Swipe);

            // invoke
            onSwipeCallback.Invoke(direction);
        }
        Logging.Log($"[{name}] スワイプ: {direction}");
    }

    //------------------------------------------------------//
}

注意点

使わないものがあればコメントアウトしてください
・ボタンの動きを加えるアセットのDOTweenを使っています
・Loggingという拡張クラス(Debugのラッパー)を使ってます

-Meisyo, MeisyoNX

執筆者:


comment

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

関連記事

[Meisyo]第2回公式戦決勝戦

名将と呼ばれた者達、第2回公式戦決勝戦がありました。結果をお伝えします。 (ゲームにログインしていれば、こちらから結果を見ることが出来ます) オモテ: 都立東京読売巨人高校 チーム総合力:115.12 …

[Meisyo+] データ分析その1 能力値ベースの打率予測

監督視点の野球ゲーム Meisyo+でデータが貯まってきたので、打率の予測をしてみました。 打率は高ければ高いほどいいですが、実際のところどの能力値を重要視していいかわかりません。 そのため、今回はど …

[Meisyo+] 試合成績の区分作成

Meisyo+でアンケートを行っています。 そのご意見をいただいた中で、より使いやすい成績利用のために成績区分を作成しました。 どんな感じになったの? 試合と特訓で成績を分けるようにしました。データ管 …

[Meisyo]ver0.04へのアップデート

Meisyoがバージョン0.04になりました。 バージョンNo.の基準はありません。(1.00(正式リリース)が遠い) 下記機能が0.04で追加されました。 目的としては、「ユーザがゲームを長く楽しめ …

[Meisyo] ver 0.31 リリース情報

名将と呼ばれた者達をver 0.31にアップデートします。 更新概要 1・投手配球設定の追加 2・練習試合マッチング方法の修正 3・課金決済の導入 4・その他 5・アンケート 更新詳細 投手配球設定の …