在处理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);
}
}