用于处理点击、键盘输入、触摸等事件,所以实际上仅仅只是InputEventSystem

EventSystem继承自UIBehaviour,而后者又继承自MonoBehaviour,所以EventSystem本质还是一个Mono

代码一开头,定义了两个输入模块

private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();
private BaseInputModule m_CurrentInputModule;

很明显,一个是所有的输入模块,一个是当前使用中的输入模块

在Update中会调用一个Tick方法,去更新所有的模块

private void TickModules()
{
    var systemInputModulesCount = m_SystemInputModules.Count;
    for (var i = 0; i < systemInputModulesCount; i++)
    {
        if (m_SystemInputModules[i] != null)
            m_SystemInputModules[i].UpdateModule();
    }
}

当前输入模块,最开始是null,会在Update的时候去判断用哪一个Module

for (var i = 0; i < systemInputModulesCount; i++)
{
    var module = m_SystemInputModules[i];
    if (module.IsModuleSupported() && module.ShouldActivateModule())
    {
        if (m_CurrentInputModule != module)
        {
            ChangeEventModule(module);
            changedModule = true;
        }
        break;
    }
}

实际上Module的继承关系有好几层,最终直接使用的有两种

  • StandaloneInputModule 用于PC
  • TouchInputModule 用于移动平台

上面两个都继承自PointerInputModule,而它最终继承自BaseInputModule

RayCastAll

public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
{
    raycastResults.Clear();
    var modules = RaycasterManager.GetRaycasters();
    var modulesCount = modules.Count;
    for (int i = 0; i < modulesCount; ++i)
    {
        var module = modules[i];
        if (module == null || !module.IsActive())
            continue;

        module.Raycast(eventData, raycastResults);
    }

    raycastResults.Sort(s_RaycastComparer);
}

其中RaycasterManager里存放里所有的BaseRayCast类,不过仅仅是一个逻辑类,实际上触摸检测是在基类里自己实现的

BaseRayCast有一个需要子类重写的方法Raycast

image-20230918153745558

UGUI里的关键就是第一个,GraphicRaycaster,这玩意一般要求挂载到Canvas上

核心代码是这一串

private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
{
    // Necessary for the event system
    int totalCount = foundGraphics.Count;
    for (int i = 0; i < totalCount; ++i)
    {
        Graphic graphic = foundGraphics[i];

        // -1 means it hasn't been processed by the canvas, which means it isn't actually drawn
        if (!graphic.raycastTarget || graphic.canvasRenderer.cull || graphic.depth == -1)
            continue;

        if (!RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera, graphic.raycastPadding))
            continue;

        if (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
            continue;

        if (graphic.Raycast(pointerPosition, eventCamera))
        {
            s_SortedGraphics.Add(graphic);
        }
    }

    s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
    totalCount = s_SortedGraphics.Count;
    for (int i = 0; i < totalCount; ++i)
        results.Add(s_SortedGraphics[i]);

    s_SortedGraphics.Clear();
}

遍历Canvas下所有能被检测的Graphic,然后依次判断是否被点击到,并添加到结果里

ExecuteEvents

这是个静态类,用于执行UGUI事件 (例如:IPointerEnterHandler)

public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler

target就是目标对象,方法会拿到target上所有需要发送事件通知的组件

GetEventList<T>(target, internalHandlers);

functor是ExecuteEvents里的一个静态属性,看一个具体调用方法

ExecuteEvents.Execute(t.gameObject, currentPointerData, ExecuteEvents.pointerEnterHandler);

意思就是你想执行什么类型的事件

最后就是遍历所有的事件目标,然后执行

public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor)
{
    ...
    GetEventList<T>(target, internalHandlers);
	...
    var internalHandlersCount = internalHandlers.Count;
    for (var i = 0; i < internalHandlersCount; i++)
    {
        T arg = (T)internalHandlers[i];
        ...
		functor(arg, eventData);
    }
	...
}

以上面那个调用方法为例,最终就会变成,这个方法就是我们调用的一个静态方法,handler就是我们自己写的接口,OnPointerEnter就是接口的具体实现了

private static void Execute(IPointerEnterHandler handler, BaseEventData eventData)
{
    handler.OnPointerEnter(ValidateEventData<PointerEventData>(eventData));
}