1. 程式人生 > >UGUI 原始碼研究及擴充套件 :EventTrigger

UGUI 原始碼研究及擴充套件 :EventTrigger

在處理Unity UI的各種響應事件的時候,一個Button往往會不太夠用,UGUI提供了對所有響應事件封裝的一個類EventTrigger,但儘量不要使用它,一是它的呼叫方式不是很友好,這種情況後文會給出一些擴充套件方法使得它可以簡單運用。第二個原因也是主要原是由於他的方法呼叫會影響效能,這一點也會在後文進行解釋。文章最後的擴充套件類也對所有的介面函式的觸發時機和順序做了註釋。先來看一下這個類原始碼的核心。

1.Triggers

首先這個類定義了一個觸發器列表,我們可以通過triggers.add(...)來新增觸發器,但是這中新增方式不太友好,需要對Entry進行型別以及回撥函式的設定,用起來比較麻煩,後面會有一系列對EventTrigger的擴充套件方法,簡化了這一流程。

private List<Entry> m_Delegates;

public List<Entry> triggers
{
    get
    {
        if (m_Delegates == null)
            m_Delegates = new List<Entry>();
        return m_Delegates;
    }
    set { m_Delegates = value; }
}
public class Entry
{
    public EventTriggerType eventID = EventTriggerType.PointerClick;
    public TriggerEvent callback = new TriggerEvent();
}

2.Excute()

EventTrigger類實現了所有Handler介面函式,並在這些函式中呼叫了Excute方法。從程式碼中我們可以看出Excute遍歷了triggers列表並比較了EventTriggerType,如果相等並且委託不為空,則執行註冊過的回撥函式。觸發器數量越多,for迴圈的次數就會越多,會造成效能上的損耗。

private void Execute(EventTriggerType id, BaseEventData eventData)
{
    for (int i = 0, imax = triggers.Count; i < imax; ++i)
    {
        var ent = triggers[i];
        if (ent.eventID == id && ent.callback != null)
            ent.callback.Invoke(eventData);
    }
}

public virtual void OnPointerEnter(PointerEventData eventData)
{
    Execute(EventTriggerType.PointerEnter, eventData);
}

public virtual void OnPointerExit(PointerEventData eventData)
{
    Execute(EventTriggerType.PointerExit, eventData);
}

public virtual void OnDrag(PointerEventData eventData)
{
    Execute(EventTriggerType.Drag, eventData);
}

public virtual void OnDrop(PointerEventData eventData)
{
    Execute(EventTriggerType.Drop, eventData);
}

public virtual void OnPointerDown(PointerEventData eventData)
{
    Execute(EventTriggerType.PointerDown, eventData);
}

public virtual void OnPointerUp(PointerEventData eventData)
{
    Execute(EventTriggerType.PointerUp, eventData);
}

public virtual void OnPointerClick(PointerEventData eventData)
{
    Execute(EventTriggerType.PointerClick, eventData);
}

public virtual void OnSelect(BaseEventData eventData)
{
    Execute(EventTriggerType.Select, eventData);
}

public virtual void OnDeselect(BaseEventData eventData)
{
    Execute(EventTriggerType.Deselect, eventData);
}

public virtual void OnScroll(PointerEventData eventData)
{
    Execute(EventTriggerType.Scroll, eventData);
}

public virtual void OnMove(AxisEventData eventData)
{
    Execute(EventTriggerType.Move, eventData);
}

public virtual void OnUpdateSelected(BaseEventData eventData)
{
    Execute(EventTriggerType.UpdateSelected, eventData);
}

public virtual void OnInitializePotentialDrag(PointerEventData eventData)
{
    Execute(EventTriggerType.InitializePotentialDrag, eventData);
}

public virtual void OnBeginDrag(PointerEventData eventData)
{
    Execute(EventTriggerType.BeginDrag, eventData);
}

public virtual void OnEndDrag(PointerEventData eventData)
{
    Execute(EventTriggerType.EndDrag, eventData);
}

public virtual void OnSubmit(BaseEventData eventData)
{
    Execute(EventTriggerType.Submit, eventData);
}

public virtual void OnCancel(BaseEventData eventData)
{
    Execute(EventTriggerType.Cancel, eventData);
}

3.擴充套件

對EventTrigger的使用可以分為兩種,一是直接在介面新增對應型別的觸發器,然後拖拽方法,這個和Button是一樣的。第二種方法是動態新增,它的大致流程如下:

private EventTrigger eventTrigger;

private void Start()
{
    eventTrigger = GetComponent<EventTrigger>();
    EventTrigger.Entry entry = new EventTrigger.Entry();   // 新建觸發器
    entry.eventID = EventTriggerType.PointerClick;         // 設定我們想要的觸發器型別,具體可以看官方API
    entry.callback.AddListener(CallBack);                  // 給觸發器添加回調函式
    eventTrigger.triggers.Add(entry);                      // 新增觸發器

}
private void CallBack(BaseEventData data)
{
    // 方法類容
}

接下來是對EventTrigger的擴充套件,通過拓展函式,直接對eventTrigger呼叫對應的add方法就可以了

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;

/// <summary>
/// 對Unity UI的擴充套件方法
/// </summary>
public static class UIExtFun
{

    /// <summary>
    /// 滑鼠進入
    /// </summary>
    public static void AddOnPointerEnter(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.PointerEnter);
    }

    /// <summary>
    /// 滑鼠移出
    /// </summary>
    public static void AddOnPointerExit(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.PointerExit);
    }

    /// <summary>
    /// 滑鼠按下
    /// </summary>
    public static void AddOnPointerDown(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.PointerDown);
    }

    /// <summary>
    /// 滑鼠擡起
    /// </summary>
    public static void AddOnPointerUp(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.PointerUp);
    }

    /// <summary>
    /// 滑鼠點選(滑鼠擡起時已不在原UI上時不會觸發,在PointerUp之後呼叫)
    /// </summary>
    public static void AddOnPointerClick(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.PointerClick);
    }

    /// <summary>
    /// 滑鼠拖拽時(滑鼠按下不移動不會觸發)
    /// </summary>
    public static void AddOnDrag(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Drag);
    }

    /// <summary>
    /// 拖拽結束時滑鼠不在被拖拽UI上並且在寧外一個UI上時觸發(在PointerUp之後)
    /// </summary>
    public static void AddOnDrop(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Drop);
    }

    /// <summary>
    /// 滑輪滾動時
    /// </summary>
    public static void AddOnScroll(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Scroll);
    }

    #region 物體被選中時並滿足相應條件觸發(用EventSystem.current.SetSelectedGameObject(gameObject)選中物體)

    /// <summary>
    /// 在被選中時
    /// </summary>
    public static void AddOnSelect(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Select);
    }

    /// <summary>
    /// 被選中後的每一幀
    /// </summary>
    public static void AddOnUpdateSelect(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.UpdateSelected);
    }

    /// <summary>
    /// 結束選中時
    /// </summary>
    public static void AddOnDeselect(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Deselect);
    }

    /// <summary>
    /// 按方向鍵時
    /// </summary>
    public static void AddOnMove(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Move);
    }
    public static void AddOnMove(this EventTrigger eventTrigger, UnityAction<BaseEventData> call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Move);
    }

    /// <summary>
    /// 預設為Enter鍵
    /// </summary>
    public static void AddOnSubmit(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Submit);
    }

    /// <summary>
    /// 預設為Esc鍵
    /// </summary>
    public static void AddOnCancel(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.Cancel);
    }

    #endregion

    /// <summary>
    /// 初始化拖拽(在PointerDown之後,PoinerUp之前呼叫,點選就會呼叫)
    /// </summary>
    public static void AddOnInitializePotentialDrag(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.InitializePotentialDrag);
    }

    /// <summary>
    /// 拖拽開始(滑鼠按下不移動不會觸發)
    /// </summary>
    public static void AddOnBeginDrag(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.BeginDrag);
    }

    /// <summary>
    /// 拖拽結束(滑鼠按下不移動不會觸發,在Drop之後)
    /// </summary>
    public static void AddOnEndDrag(this EventTrigger eventTrigger, UnityAction call)
    {
        AddCall(eventTrigger, call, EventTriggerType.EndDrag);
    }

    /// <summary>
    /// 給EventTrigger新增Entry
    /// </summary>
    private static void AddCall(EventTrigger eventTrigger, UnityAction call, EventTriggerType type)
    {
        EventTrigger.Entry myclick = new EventTrigger.Entry
        {
            eventID = type,
        };

        // ?.操作符是c#6.0提供的新特性,需要將工程升級到.net4.x,具體可以自己查閱相關資訊
        myclick.callback.AddListener((BaseEventData data) => { call?.Invoke(); });
        eventTrigger.triggers.Add(myclick);
    }

    /// <summary>
    /// 提供一種開放BaseEventData的過載
    /// 需要BaseEventData的事件也要寫過載,上面我只對AddOnMove做了過載
    /// </summary>
    private static void AddCall(EventTrigger eventTrigger, UnityAction<BaseEventData> call, EventTriggerType type)
    {
        EventTrigger.Entry myclick = new EventTrigger.Entry
        {
            eventID = type,
        };
        myclick.callback.AddListener((BaseEventData data) => { call?.Invoke(data); });
        eventTrigger.triggers.Add(myclick);
    }
}