管理Unity GUI的按鈕點擊事件

或許有人跟我一樣不太喜歡Unity內建的Button。

除了操作步驟有點繁瑣之外,為了指派點擊事件的呼叫對象
還必須要將對應的函式宣告為public成員。

對於隱匿性來說總覺得不那麼恰當。

於是我另外寫了一個針對游標點擊事件的腳本用來解決上述的需求。
沒時間了快上 code!



*************************************************
* Description
* 偵測UGUI物件的游標點擊事件
* 
* Usage 
* 呼叫Init並且代入掛載對象與游標事件的呼叫函式
* 在Init函式中可以指定索引
* 可以額外儲存單個Image與Text
**************************************************/

using System;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;

public class UGUIClickListener : MonoBehaviour, IPointerClickHandler
{

    #region >>  Fields & Properties

    [SerializeField]
    [Header("索引編號")]
    int index;

    public int Index
    {
        get { return index; }
    }

    [SerializeField]
    [Header("圖像")]
    Image image;

    public Image Image
    {
        get { return image; }
    }

    [SerializeField]
    [Header("文字")]
    Text text;

    public Text Text
    {
        get { return text; }
    }

    Action<UGUIClickListener> pointerClicked;

    #endregion


    /// <summary>
    /// 將代入的物件添加Component,並且指派游標事件的呼叫函式
    /// </summary>
    /// <param name="go">要添加腳本的物件對象</param>
    /// <param name="action">事件的呼叫對象</param>
    /// <param name="id">設置編號索引</param>
    public static UGUIClickListener Init(GameObject go, Action<UGUIClickListener> action, int id = 0)
    {
        UGUIClickListener listener = go.GetComponent<UGUIClickListener>();
        if(listener == null)
        {
            listener = go.AddComponent<UGUIClickListener>();
        }

        listener.index = id;

        listener.pointerClicked= action;

        return listener;
    }


    /// <summary>
    /// 儲存Image物件
    /// </summary>
    public UGUIClickListener SetImage(Image input)
    {
        image = input;
        return this;
    }


    /// <summary>
    /// 儲存Text物件
    /// </summary>
    public UGUIClickListener SetText(Text input)
    {
        text = input;
        return this;
    }


    /// <summary>
    /// IPointerClickHandler的介面實作,偵測游標對於掛載物件的Click事件
    /// </summary>
    public void OnPointerClick(PointerEventData data)
    {
        if(PointerClicked != null)
        {
            pointerClicked(this);
        }
    }

}



在這個型別中,我們可以呼叫Init代入要掛載的點擊對象,
並且指派點擊事件發生之後的呼叫函式。

當掛載的物件發生點擊事件時,OnPointerClick函式將會被呼叫
之後再藉由pointerClicked呼叫委派的函式就可以了。

使用範例如下:
using UnityEngine;
using UnityEngine.UI;

public class ButtonClickSample : MonoBehaviour
{

    public Image[] buttons;

    void Start() {
        for(int i = 0; i < buttons.Length; i++) {
            UGUIClickListener.Init(buttons[i].gameObject, ButtonClickEvent, i);
        }
    }

    void ButtonClickEvent(UGUIClickListener ev) {
        Debug.Log("Button Clicked: " + ev.Index);
    }
}



假使有一些特殊狀況無法藉由Index判斷對象,
並且想要快速取得對應按鈕事件的某個Image或者Text,
在呼叫Init函式的時候則可以另外儲存對象。

使用範例如下:
using UnityEngine;
using UnityEngine.UI;

public class ButtonClickSample : MonoBehaviour
{
    [SerializeField] CanvasGroup canv;
    [SerializeField] Image[] buttons;

    void Start() {
        for(int i = 0; i < buttons.Length; i++) {
            UGUIClickListener.Init(buttons[i].gameObject, ButtonClickEvent).SetImage(buttons[i]);
        }
    }

    void ButtonClickEvent(UGUIClickListener ev) {
        UnlockOpration(false);
        ev.Image.color = Color.gray;
    }

    void UnlockOpration(bool enable = true) {
        canv.blocksRaycasts = enable;
    }
}


在上方的範例中,我順便加了一個鎖定操控的機制。

當任何按鈕被按下時,在canv底下的按鈕都會無法接收到游標事件,
直到UnlockOpration函式再次被呼叫為止。

目前為止改用UGUI系統已經開發過了幾個專案,
到現在都一直使用這一套方式來管理點擊的事件。

儲存Image與Text則是近期需求才擴增的,
這裡可以依照專案需求去增減儲存的物件。

若還要感應其他的游標事件如游標的滑入滑出,拖曳等等,
我比較偏向以類似的架構另外寫一個新的腳本,避免EventSystems呼叫到不必要的動作。

留言

熱門文章