美文网首页Unity干货
Unity UGUI事件系统源码简单剖析

Unity UGUI事件系统源码简单剖析

作者: 阿飞咯 | 来源:发表于2020-03-01 18:00 被阅读0次

    调用堆栈
    EventSystem.Update -> BaseInputModule.Process -> StandaloneInputModule.Process ->StandaloneInputModule.ProcessTouchEvents

            //Event.Update
            protected virtual void Update()
            {
                if (current != this)
                    return;
                TickModules();
    
                bool changedModule = false;
                for (var i = 0; i < m_SystemInputModules.Count; i++)
                {
                    var module = m_SystemInputModules[i];
                    if (module.IsModuleSupported() && module.ShouldActivateModule())
                    {
                        if (m_CurrentInputModule != module)
                        {
                            ChangeEventModule(module);
                            changedModule = true;
                        }
                        break;
                    }
                }
    
                // no event module set... set the first valid one...
                if (m_CurrentInputModule == null)
                {
                    for (var i = 0; i < m_SystemInputModules.Count; i++)
                    {
                        var module = m_SystemInputModules[i];
                        if (module.IsModuleSupported())
                        {
                            ChangeEventModule(module);
                            changedModule = true;
                            break;
                        }
                    }
                }
                //调用InputModule的Process方法
                if (!changedModule && m_CurrentInputModule != null)
                    m_CurrentInputModule.Process();
            }
    
            //StandaloneInputModule .Process
            public override void Process()
            {
                if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
                    return;
    
                bool usedEvent = SendUpdateEventToSelectedObject();
    
                if (eventSystem.sendNavigationEvents)
                {
                    if (!usedEvent)
                        usedEvent |= SendMoveEventToSelectedObject();
    
                    if (!usedEvent)
                        SendSubmitEventToSelectedObject();
                }
    
                // touch needs to take precedence because of the mouse emulation layer
                //处理事件
                if (!ProcessTouchEvents() && input.mousePresent)
                    ProcessMouseEvent();
            }
    
            //StandaloneInputModule .ProcessTouchEvents
            private bool ProcessTouchEvents()
            {
                for (int i = 0; i < input.touchCount; ++i)
                {
                    Touch touch = input.GetTouch(i);
    
                    if (touch.type == TouchType.Indirect)
                        continue;
    
                    bool released;
                    bool pressed;
                    var pointer = GetTouchPointerEventData(touch, out pressed, out released); //获取当前事件响应的对象
     pressed和released分边表示是否为事件开始和事件结束
                    
                    ProcessTouchPress(pointer, pressed, released);//分别处理事件的开端和结束
                    if (!released)
                    {
                        ProcessMove(pointer);//处理move事件
                        ProcessDrag(pointer);//处理drag事件
                    }
                    else
                        RemovePointerData(pointer);
                }
                return input.touchCount > 0;
            }
    
    • 获取事件响应raycast对象,并返回该事件是否为开始或结束事件
            /// <summary>
            /// Given a touch populate the PointerEventData and return if we are pressed or released.
            /// </summary>
            /// <param name="input">Touch being processed</param>
            /// <param name="pressed">Are we pressed this frame</param>
            /// <param name="released">Are we released this frame</param>
            /// <returns></returns>
            protected PointerEventData GetTouchPointerEventData(Touch input, out bool pressed, out bool released)
            {
                PointerEventData pointerData;
                var created = GetPointerData(input.fingerId, out pointerData, true);
    
                pointerData.Reset();
    
                pressed = created || (input.phase == TouchPhase.Began);
                released = (input.phase == TouchPhase.Canceled) || (input.phase == TouchPhase.Ended);
    
                if (created)
                    pointerData.position = input.position;
    
                if (pressed)
                    pointerData.delta = Vector2.zero;
                else
                    pointerData.delta = input.position - pointerData.position;
    
                pointerData.position = input.position;
    
                pointerData.button = PointerEventData.InputButton.Left;
    
                if (input.phase == TouchPhase.Canceled)
                {
                    pointerData.pointerCurrentRaycast = new RaycastResult();
                }
                else
                {
                    eventSystem.RaycastAll(pointerData, m_RaycastResultCache);//raycast获取当前点所有可响应的对象,UGUI通过GraphicRaycaster获取canvas下面所有勾选了Raycast的Graphic(graphic.Raycast),并将所有的对象按照深度排序
    
                    var raycast = FindFirstRaycast(m_RaycastResultCache);//最终只取第一个响应对象
                    pointerData.pointerCurrentRaycast = raycast;
                    m_RaycastResultCache.Clear();
                }
                return pointerData;
            }
    
    • 处理初始化事件点和释放事件点
    /// <summary>
            /// This method is called by Unity whenever a touch event is processed. Override this method with a custom implementation to process touch events yourself.
            /// </summary>
            /// <param name="pointerEvent">Event data relating to the touch event, such as position and ID to be passed to the touch event destination object.</param>
            /// <param name="pressed">This is true for the first frame of a touch event, and false thereafter. This can therefore be used to determine the instant a touch event occurred.</param>
            /// <param name="released">This is true only for the last frame of a touch event.</param>
            /// <remarks>
            /// This method can be overridden in derived classes to change how touch press events are handled.
            /// </remarks>
            protected void ProcessTouchPress(PointerEventData pointerEvent, bool pressed, bool released)
            {
                var currentOverGo = pointerEvent.pointerCurrentRaycast.gameObject;
    
                // PointerDown notification
                if (pressed)
                {
                    pointerEvent.eligibleForClick = true;
                    pointerEvent.delta = Vector2.zero;
                    pointerEvent.dragging = false;
                    pointerEvent.useDragThreshold = true;
                    pointerEvent.pressPosition = pointerEvent.position;
                    pointerEvent.pointerPressRaycast = pointerEvent.pointerCurrentRaycast;
    
                    DeselectIfSelectionChanged(currentOverGo, pointerEvent);
    
                    if (pointerEvent.pointerEnter != currentOverGo)
                    {
                        // send a pointer enter to the touched element if it isn't the one to select...
                        HandlePointerExitAndEnter(pointerEvent, currentOverGo);
                        pointerEvent.pointerEnter = currentOverGo;
                    }
                    
                     //获取并执行实现pointerDownHandler的对象方法
                    // search for the control that will receive the press
                    // if we can't find a press handler set the press
                    // handler to be what would receive a click.
                    var newPressed = ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.pointerDownHandler);
                    //从当前响应Graphic对象节点冒泡寻找第一个实现IPointerClickHandler的脚本对象
                    // didnt find a press handler... search for a click handler
                    if (newPressed == null)
                        newPressed = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
    
                    // Debug.Log("Pressed: " + newPressed);
    
                    float time = Time.unscaledTime;
    
                    if (newPressed == pointerEvent.lastPress)
                    {
                        var diffTime = time - pointerEvent.clickTime;
                        if (diffTime < 0.3f)
                            ++pointerEvent.clickCount;
                        else
                            pointerEvent.clickCount = 1;
    
                        pointerEvent.clickTime = time;
                    }
                    else
                    {
                        pointerEvent.clickCount = 1;
                    }
    
                    pointerEvent.pointerPress = newPressed;
                    pointerEvent.rawPointerPress = currentOverGo;
    
                    pointerEvent.clickTime = time;
    
                    // Save the drag handler as well
                    pointerEvent.pointerDrag = ExecuteEvents.GetEventHandler<IDragHandler>(currentOverGo);
    
                    if (pointerEvent.pointerDrag != null)
                        ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.initializePotentialDrag);
    
                    m_InputPointerEvent = pointerEvent;
                }
    
                // PointerUp notification
                if (released)
                {
                    // Debug.Log("Executing pressup on: " + pointer.pointerPress);
                    ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
    
                    // Debug.Log("KeyCode: " + pointer.eventData.keyCode);
    
                    // see if we mouse up on the same element that we clicked on...
                    var pointerUpHandler = ExecuteEvents.GetEventHandler<IPointerClickHandler>(currentOverGo);
    
                    // PointerClick and Drop events
                    if (pointerEvent.pointerPress == pointerUpHandler && pointerEvent.eligibleForClick)
                    {
                        ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerClickHandler);
                    }
                    else if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
                    {
                        ExecuteEvents.ExecuteHierarchy(currentOverGo, pointerEvent, ExecuteEvents.dropHandler);
                    }
    
                    pointerEvent.eligibleForClick = false;
                    pointerEvent.pointerPress = null;
                    pointerEvent.rawPointerPress = null;
    
                    if (pointerEvent.pointerDrag != null && pointerEvent.dragging)
                        ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.endDragHandler);
    
                    pointerEvent.dragging = false;
                    pointerEvent.pointerDrag = null;
    
                    // send exit events as we need to simulate this on touch up on touch device
                    ExecuteEvents.ExecuteHierarchy(pointerEvent.pointerEnter, pointerEvent, ExecuteEvents.pointerExitHandler);
                    pointerEvent.pointerEnter = null;
    
                    m_InputPointerEvent = pointerEvent;
                }
            }
    
    

    //处理拖拽事件

            /// <summary>
            /// Process the drag for the current frame with the given pointer event.
            /// </summary>
            protected virtual void ProcessDrag(PointerEventData pointerEvent)
            {
                if (!pointerEvent.IsPointerMoving() ||
                    Cursor.lockState == CursorLockMode.Locked ||
                    pointerEvent.pointerDrag == null)
                    return;
    
                if (!pointerEvent.dragging
                    && ShouldStartDrag(pointerEvent.pressPosition, pointerEvent.position, eventSystem.pixelDragThreshold, pointerEvent.useDragThreshold))
                {
                    ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.beginDragHandler);
                    pointerEvent.dragging = true;
                }
    
                // Drag notification
                if (pointerEvent.dragging)
                {
                    // Before doing drag we should cancel any pointer down state
                    // And clear selection!
                    if (pointerEvent.pointerPress != pointerEvent.pointerDrag)
                    {
                        ExecuteEvents.Execute(pointerEvent.pointerPress, pointerEvent, ExecuteEvents.pointerUpHandler);
    
                        pointerEvent.eligibleForClick = false;
                        pointerEvent.pointerPress = null;
                        pointerEvent.rawPointerPress = null;
                    }
                    ExecuteEvents.Execute(pointerEvent.pointerDrag, pointerEvent, ExecuteEvents.dragHandler);
                }
            }
    

    相关文章

      网友评论

        本文标题:Unity UGUI事件系统源码简单剖析

        本文链接:https://www.haomeiwen.com/subject/quwckhtx.html