上一篇介绍了PointerEventData类的字段及其用途,这一篇来详细说明它们是怎样运作的。
关键接口IEventSystemHandler
Unity通过继承关键接口IEventSystemHandler的接口,来定义了所有用于处理触摸、点击的事件。
IPointerEnterHandler 进入事件,IPointerExitHandler 离开事件,IPointerDownHandler 按下事件,IPointerUpHandler 抬起事件,IPointerClickHandler 点击事件,IInitializePotentialDragHandler 找到可拖动对象后, 真正开始拖动之前的事件, 整个拖动过程中只发送一次,IBeginDragHandler 开始拖动事件,IDragHandler 拖动事件,IEndDragHandler 结束拖动事件,IDropHandler 拖动并放开事件,IScrollHandler 滚动事件,IUpdateSelectedHandler 更新选中事件,ISelectHandler 切换选中事件,IDeselectHandler 取消选中事件,IMoveHandler 导航移动事件,ISubmitHandler 导航提交事件,ICancelHandler 导航取消事件
关键类EventTrigger
还可以通过EventTrigger类来实现上面接口的效果,EventTrigger类借助枚举EventTriggerType,与上面的接口一一对应。
public enum EventTriggerType
{
/// <summary>
/// Intercepts a IPointerEnterHandler.OnPointerEnter.
/// </summary>
PointerEnter = 0,
/// <summary>
/// Intercepts a IPointerExitHandler.OnPointerExit.
/// </summary>
PointerExit = 1,
/// <summary>
/// Intercepts a IPointerDownHandler.OnPointerDown.
/// </summary>
PointerDown = 2,
/// <summary>
/// Intercepts a IPointerUpHandler.OnPointerUp.
/// </summary>
PointerUp = 3,
/// <summary>
/// Intercepts a IPointerClickHandler.OnPointerClick.
/// </summary>
PointerClick = 4,
/// <summary>
/// Intercepts a IDragHandler.OnDrag.
/// </summary>
Drag = 5,
/// <summary>
/// Intercepts a IDropHandler.OnDrop.
/// </summary>
Drop = 6,
/// <summary>
/// Intercepts a IScrollHandler.OnScroll.
/// </summary>
Scroll = 7,
/// <summary>
/// Intercepts a IUpdateSelectedHandler.OnUpdateSelected.
/// </summary>
UpdateSelected = 8,
/// <summary>
/// Intercepts a ISelectHandler.OnSelect.
/// </summary>
Select = 9,
/// <summary>
/// Intercepts a IDeselectHandler.OnDeselect.
/// </summary>
Deselect = 10,
/// <summary>
/// Intercepts a IMoveHandler.OnMove.
/// </summary>
Move = 11,
/// <summary>
/// Intercepts IInitializePotentialDrag.InitializePotentialDrag.
/// </summary>
InitializePotentialDrag = 12,
/// <summary>
/// Intercepts IBeginDragHandler.OnBeginDrag.
/// </summary>
BeginDrag = 13,
/// <summary>
/// Intercepts IEndDragHandler.OnEndDrag.
/// </summary>
EndDrag = 14,
/// <summary>
/// Intercepts ISubmitHandler.Submit.
/// </summary>
Submit = 15,
/// <summary>
/// Intercepts ICancelHandler.OnCancel.
/// </summary>
Cancel = 16
}
最简单的使用EventTrigger:
void Start()
{
EventTrigger eventTrigger = GetComponent<EventTrigger>();
if (!eventTrigger) eventTrigger = gameObject.AddComponent<EventTrigger>();
var entry1 = new EventTrigger.Entry();
entry1.eventID = EventTriggerType.PointerClick;//可修改类型
//等同于在物体的Monobehavior继承IPointerClickHandler
entry1.callback.AddListener((eventData => {
var pointerEventData = eventData as PointerEventData;
Debug.LogError("----- PointerClick");
}));
}
触摸、点击的运行逻辑
通过ExecuteEvents这个静态事件类,执行Execute方法,例如 OnPointerEnter进入事件:
public static class ExecuteEvents
{
public delegate void EventFunction<T1>(T1 handler, BaseEventData eventData);
public static T ValidateEventData<T>(BaseEventData data) where T : class
{
if ((data as T) == null)
throw new ArgumentException(String.Format("Invalid type: {0} passed to event expecting {1}", data.GetType(), typeof(T)));
return data as T;
}
private static readonly EventFunction<IPointerEnterHandler> s_PointerEnterHandler = Execute;
private static void Execute(IPointerEnterHandler handler, BaseEventData eventData)
{
handler.OnPointerEnter(ValidateEventData<PointerEventData>(eventData));
}
}
类似的,对之前提到的所有的事件类型,都提供了对应的封装事件触发方法。通过从对象身上获取事件处理器(也就是收集之前通过接口或者EventTrigger设置的事件绑定组件),先判断是否有效,
private static bool ShouldSendToComponent<T>(Component component) where T : IEventSystemHandler
{
var valid = component is T;
if (!valid)
return false;
var behaviour = component as Behaviour;
if (behaviour != null)
return behaviour.isActiveAndEnabled;
return true;
}
如果有效,在游戏对象上冒泡指定的事件,找出哪个对象将实际接收该事件,也就是找到第一个符合要求的handler
/// <summary>
/// Whether the specified game object will be able to handle the specified event.
/// </summary>
public static bool CanHandleEvent<T>(GameObject go) where T : IEventSystemHandler
{
var internalHandlers = ListPool<IEventSystemHandler>.Get();
GetEventList<T>(go, internalHandlers);
var handlerCount = internalHandlers.Count;
ListPool<IEventSystemHandler>.Release(internalHandlers);
return handlerCount != 0;
}
/// <summary>
/// Bubble the specified event on the game object, figuring out which object will actually receive the event.
/// </summary>
public static GameObject GetEventHandler<T>(GameObject root) where T : IEventSystemHandler
{
if (root == null)
return null;
Transform t = root.transform;
while (t != null)
{
if (CanHandleEvent<T>(t.gameObject))
return t.gameObject;
t = t.parent;
}
return null;
}
执行 Execute方法然后根据事件类型对应到具体的事件类型的Execute方法,由于每帧获取handler很消耗性能,因此做了对象池逻辑来降低消耗。
public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
{
var internalHandlers = ListPool<IEventSystemHandler>.Get();
GetEventList<T>(target, internalHandlers);
// if (s_InternalHandlers.Count > 0)
// Debug.Log("Executinng " + typeof (T) + " on " + target);
var internalHandlersCount = internalHandlers.Count;
for (var i = 0; i < internalHandlersCount; i++)
{
T arg;
try
{
arg = (T)internalHandlers[i];
}
catch (Exception e)
{
var temp = internalHandlers[i];
Debug.LogException(new Exception(string.Format("Type {0} expected {1} received.", typeof(T).Name, temp.GetType().Name), e));
continue;
}
try
{
functor(arg, eventData);
}
catch (Exception e)
{
Debug.LogException(e);
}
}
var handlerCount = internalHandlers.Count;
ListPool<IEventSystemHandler>.Release(internalHandlers);
return handlerCount > 0;
}
/// <summary>
/// Get the specified object's event event.
/// </summary>
private static void GetEventList<T>(GameObject go, IList<IEventSystemHandler> results) where T : IEventSystemHandler
{
// Debug.LogWarning("GetEventList<" + typeof(T).Name + ">");
if (results == null)
throw new ArgumentException("Results array is null", "results");
if (go == null || !go.activeInHierarchy)
return;
var components = ListPool<Component>.Get();
go.GetComponents(components);
var componentsCount = components.Count;
for (var i = 0; i < componentsCount; i++)
{
if (!ShouldSendToComponent<T>(components[i]))
continue;
// Debug.Log(string.Format("{2} found! On {0}.{1}", go, s_GetComponentsScratch[i].GetType(), typeof(T)));
results.Add(components[i] as IEventSystemHandler);
}
ListPool<Component>.Release(components);
// Debug.LogWarning("end GetEventList<" + typeof(T).Name + ">");
}
总结
这一篇介绍了事件系统的一些定义和消息的传递机制,对于整个触摸、点击事件的实现有了进一步的了解。