源码3:ExecuteEvents 上面在分析 输入模块的时候 都提到了会有对应的事件发送。具体的实现就在ExecuteEvents 类里面
下面我们来分析一下这个事件系统
在最上面 有一个委托定义
public delegate void EventFunction(T1 handler, BaseEventData eventData);
带有两个参数 一个是handler 是个泛型 一个是eventData通常这个会包含位置信息 偏移量 点击物体等等
基于这个委托 在当前类定义了很多委托的实例 (下面放了部分原始代码)
private static readonly EventFunction s_PointerEnterHandler = Execute;
private static void Execute(IPointerEnterHandler handler, BaseEventData eventData)
{
handler.OnPointerEnter(ValidateEventData(eventData));
}private static readonly EventFunction s_PointerExitHandler = Execute;
private static void Execute(IPointerExitHandler handler, BaseEventData eventData)
{
handler.OnPointerExit(ValidateEventData(eventData));
}private static readonly EventFunction s_PointerDownHandler = Execute;
private static void Execute(IPointerDownHandler handler, BaseEventData eventData)
{
handler.OnPointerDown(ValidateEventData(eventData));
}private static readonly EventFunction s_PointerUpHandler = Execute;
private static void Execute(IPointerUpHandler handler, BaseEventData eventData)
{
handler.OnPointerUp(ValidateEventData【Unity3D_UGUI|UIGU源码分析3 (ExecuteEvents)】(eventData));
}
从这里看出来 上面有很多具体的handler类型,这些handler 也都是一个interface 并且是继承IEventSystemHandler 。同时定义了自己的具体方法。
所以从这里就不难看出来 UI上的具体行为事件 就是通过调用这些handler接口实现的。(上面代码看不明白的就得去熟悉下c# 委托相关的类容)
Execute 具体调用上面的委托实例就是通过下面代码实现了
private static readonly ObjectPool> s_HandlerListPool = new ObjectPool>(null, l => l.Clear());
public static bool Execute(GameObject target, BaseEventData eventData, EventFunction functor) where T : IEventSystemHandler
{
var internalHandlers = s_HandlerListPool.Get();
GetEventList(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;
s_HandlerListPool.Release(internalHandlers);
return handlerCount > 0;
}
上面代码思路是:
1.定义一个对象池 每次执行前 先从对象池中获取List ,上面讲过所有的操作事件都是IEventSystemHandler接口的子类
2.获取当前对象上所有的IEventSystemHandler 返回到上面的List集合中
///
/// Get the specified object's event event.
///
private static void GetEventList(GameObject go, IList 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.Get();
go.GetComponents(components);
var componentsCount = components.Count;
for (var i = 0;
i < componentsCount;
i++)
{
if (!ShouldSendToComponent(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.Release(components);
3.遍历获取到的IEventSystemHandler ,将handler作为第一个参数,eventData作为第二个参数 执行EventFunction方法,也就是上面讲的委托实例。它会调用handler中对应接口的方法
例如在TouchInputModule中我们调用松开事件
ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
ExecuteHierarchy 与Execute方法类似 这里面还定义了一个ExecuteHierarchy执行事件方法
///
/// Execute the specified event on the first game object underneath the current touch.
///
private static readonly List s_InternalTransformList = new List(30);
public static GameObject ExecuteHierarchy(GameObject root, BaseEventData eventData, EventFunction callbackFunction) where T : IEventSystemHandler
{
GetEventChain(root, s_InternalTransformList);
var internalTransformListCount = s_InternalTransformList.Count;
for (var i = 0;
i < internalTransformListCount;
i++)
{
var transform = s_InternalTransformList[i];
if (Execute(transform.gameObject, eventData, callbackFunction))
return transform.gameObject;
}
return null;
}
private static void GetEventChain(GameObject root, IList eventChain)
{
eventChain.Clear();
if (root == null)
return;
var t = root.transform;
while (t != null)
{
eventChain.Add(t);
t = t.parent;
}
}
实际上他最终的执行还是通过Execute 方法 ,不过不同点就在与他执行前先沿着Hierarchy获取了包括当前物体以及往上的所有父对象。然后遍历查询这些对项 直到有handler为止。
不过可以想象 这样做的话 下层层级对象的事件肯定会阻挡上层层级对象的事件。
其他方法
//判断对象能否接受对应事件
///
/// Whether the specified game object will be able to handle the specified event.
///
public static bool CanHandleEvent(GameObject go) where T : IEventSystemHandler
{
var internalHandlers = s_HandlerListPool.Get();
GetEventList(go, internalHandlers);
var handlerCount = internalHandlers.Count;
s_HandlerListPool.Release(internalHandlers);
return handlerCount != 0;
}//获取实际能接受对应事件的对象
///
/// Bubble the specified event on the game object, figuring out which object will actually receive the event.
///
public static GameObject GetEventHandler(GameObject root) where T : IEventSystemHandler
{
if (root == null)
return null;
Transform t = root.transform;
while (t != null)
{
if (CanHandleEvent(t.gameObject))
return t.gameObject;
t = t.parent;
}
总结 总的来说这套事件系统还是比较清晰,基于这套我们自己也能扩充更多的事件
推荐阅读
- Unity3D 2021.1.2F1 发布了。赋国际版本下载地址。
- unity3d LTS(2018-2020)国际版本下载与安装。
- 读书|读高质量C++/C编程指南第6章
- 数据结构|数据结构 - 堆
- linux|Linux基本指令
- 读书|读高质量C++/C编程指南第4章
- 读书|读高质量C++/C编程指南1-3章
- 数据结构|八大经典排序算法
- c语言|5、C语言队列与应用