C#

阅读UGUI源码

事件机制

Posted by Bob on September 21, 2020
[Selectable]

Button/Dropdown/InputField/Scrollbar/Slider/Toggle都是继承Selectable实现,Transition实现状态间切换。

public class Selectable
        :
        UIBehaviour,
        IMoveHandler,
        IPointerDownHandler, IPointerUpHandler,
        IPointerEnterHandler, IPointerExitHandler,
        ISelectHandler, IDeselectHandler
    {}

protected enum SelectionState
{
    Normal,
    Highlighted,
    Pressed,
    Disabled
}

public enum Transition
{
    None,
    ColorTint,
    SpriteSwap,
    Animation
}
[EventSystem]

EventSystem管理所有BaseInputModule,系统支持且激活启用的Module被调用Process,PointerEventData和Raycast的结果传给Module去处理,BaseInputModule通知具体的IEventSystemHandler进行逻辑处理(ExecuteEvents)。

EventData:

BaseEventData实现AbstractEventData

PointerEventData:点击触摸相关数据

AxisEventData:移动方向和量

[StandaloneInputModule]

StandaloneInputModule->PointerInputModule->BaseInputModule

ProcessMouseEvent:鼠标左中右键处理ProcessMousePress,ProcessMove,ProcessDrag

ProcessTouchEvents:多点触摸处理ProcessTouchPress,ProcessMove,ProcessDrag

均产生PointerEventData符合条件的交予ExecuteEvents执行事件。

[Raycasters]

如下图可可知事件处理中,性能最大消耗点在BaseRaycaster.Raycast image

如下代码可知即使未勾选raycastTarget也会参与到遍历中,访问频次集中在depth和RectangleContainsScreenPoint以及sort排序。


        private static void Raycast(Canvas canvas, Camera eventCamera, Vector2 pointerPosition, IList<Graphic> foundGraphics, List<Graphic> results)
        {
            // Debug.Log("ttt" + pointerPoision + ":::" + camera);
            // 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.depth == -1 || !graphic.raycastTarget || graphic.canvasRenderer.cull)
                    continue;

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

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

            s_SortedGraphics.Sort((g1, g2) => g2.depth.CompareTo(g1.depth));
            //      StringBuilder cast = new StringBuilder();
            totalCount = s_SortedGraphics.Count;
            for (int i = 0; i < totalCount; ++i)
                results.Add(s_SortedGraphics[i]);
            //      Debug.Log (cast.ToString());

            s_SortedGraphics.Clear();
        }
    }

[EventSystemHandler]

事件处理者,需要触发哪个事件就实现哪个接口

主要分为三大类:

IPointerXXXXXHandler

IXXXXXDragHandler

IXXXXXHandler

[ExecuteEvents]

遍历所有实现的事件接口,执行事件。


public static bool Execute<T>(GameObject target, BaseEventData eventData, EventFunction<T> functor) where T : IEventSystemHandler
        {
            var internalHandlers = s_HandlerListPool.Get();
            GetEventList<T>(target, internalHandlers);
            //  if (s_InternalHandlers.Count > 0)
            //      Debug.Log("Executinng " + typeof (T) + " on " + target);

            for (var i = 0; i < internalHandlers.Count; 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;
        }

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);
            for (var i = 0; i < components.Count; 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 + ">");
        }