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]試合一球分析用DBの追加

試合で一球ごとの分析用DBを追加しました。 なぜかというと、試合での投手vs打者における投球と打撃結果の因果関係が分からなかったからです。 もっとカンタンニシテ・・・ 例えば・・・ どのコースは打たれ …

[Meisyo]強化合成の追加とミニゲーム1種類の追加

名将と呼ばれた者達よりお知らせです。 強化合成を実装しました。 強化合成とは 選手たちの好きな能力を強化します。 強化値は+0~5です。能力値にそのまま反映されます。 +2(+3への)以降の強化は、失 …

[Meisyo]育成情報

これは何? 名将と呼ばれた者達(略:Meisyo)の説明書です。 まず何をやればいいの?というところから、 どうすれば上手くいくの?という疑問にお答えします。 質問や気になるところがあれば、れいまでご …

[Meisyo] アップデート方針2020.10

今後のアップデート方針を記載します。中々今回もボリュームがありますね。 今後の方針 1・新規向け:新規登録後の継続者を増やす  1・データ分析   基盤ができているので、まずは離脱ポイントがないかか確 …

[Meisyo]模擬戦の追加(テスト)vs 大阪桐蔭2018

模擬戦(NPC勝ち抜き戦)を追加しました! 模擬戦のコンセプトは「甲子園歴代優勝校と戦える場を」です。 経験値高め。もらえるアイテムも多いです。 挑戦には練習試合で7日に1回くらい拾える「試合チケット …