梳理Unity EventSystem事件系统调用过程

之前写过一个关于Button点击事件怎么被调用的,这次把EventSystem事件系统调用过程总结一下

梳理Unity EventSystem事件系统调用过程_第1张图片

                 图来自 UGUI源码分析:EventSystem事件系统_Vin129的博客-CSDN博客

在事件系统中,最重要的两个类是EventSystem与StandaloneInputModule,这两个类均继承自基类UIBehavior,而UIBehavior继承了MonoBehavior,因此这两个类是以组件的形式在Unity中执行逻辑的.

public abstract class UIBehaviour : MonoBehaviour
    {
        protected virtual void Awake()
        {}

        protected virtual void OnEnable()
        {}

        protected virtual void Start()
        {}

        protected virtual void OnDisable()
        {}

        protected virtual void OnDestroy()
        {}

        /// 
        /// Returns true if the GameObject and the Component are active.
        /// 
        public virtual bool IsActive()
        {
            return isActiveAndEnabled;
        }

#if UNITY_EDITOR
        protected virtual void OnValidate()
        {}

        protected virtual void Reset()
        {}
#endif
        /// 
        /// This callback is called if an associated RectTransform has its dimensions changed. The call is also made to all child rect transforms, even if the child transform itself doesn't change - as it could have, depending on its anchoring.
        /// 
        protected virtual void OnRectTransformDimensionsChange()
        {}

        protected virtual void OnBeforeTransformParentChanged()
        {}

        protected virtual void OnTransformParentChanged()
        {}

        protected virtual void OnDidApplyAnimationProperties()
        {}

        protected virtual void OnCanvasGroupChanged()
        {}

        /// 
        /// Called when the state of the parent Canvas is changed.
        /// 
        protected virtual void OnCanvasHierarchyChanged()
        {}

        /// 
        /// Returns true if the native representation of the behaviour has been destroyed.
        /// 
        /// 
        /// When a parent canvas is either enabled, disabled or a nested canvas's OverrideSorting is changed this function is called. You can for example use this to modify objects below a canvas that may depend on a parent canvas - for example, if a canvas is disabled you may want to halt some processing of a UI element.
        /// 
        public bool IsDestroyed()
        {
            // Workaround for Unity native side of the object
            // having been destroyed but accessing via interface
            // won't call the overloaded ==
            return this == null;
        }
    }

首先对于EventSystem,它其中有BaseInputModule m_CurrentInputModule与 List m_SystemInputModules均是管理输入模块的,默认情况下Unity与含有EventSystem的物体一同生成StandaloneInputModule

对于EventSystem中List m_SystemInputModules中的对象添加是在BaseInputModule类中

        protected override void OnEnable()
        {
            base.OnEnable();
            m_EventSystem = GetComponent();
            m_EventSystem.UpdateModules();
        }

        BaseInputModule中的OnEnable()方法


        public void UpdateModules()
        {
            GetComponents(m_SystemInputModules);
            for (int i = m_SystemInputModules.Count - 1; i >= 0; i--)
            {
                if (m_SystemInputModules[i] && m_SystemInputModules[i].IsActive())
                    continue;

                m_SystemInputModules.RemoveAt(i);
            }
        }

        EventSystem中的OnEnable()方法

在BaseInputModule调用EventSystem的UpdateModules方法之后将对List m_SystemInputModules进行填充,而StandaloneInputModule继承自BaseInputModule且StandaloneInputModule与EventSystem在同一个物体上,因此StandaloneInputModule被加入到EventSystem中的List m_SystemInputModules中之后进行轮询访问.

在EventSystem中其内部的Update方法进行帧调用每一个BaseInputModule对应的方法

 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;
                    }
                }
            }

            if (!changedModule && m_CurrentInputModule != null)
                m_CurrentInputModule.Process();
        }

这里current为EventSystem类,对于EventSystem一个场景只能存在一个,EventSystem被自身类静态存储      private static List m_EventSystems = new List();

对于TickModules方法,该方法进行轮询上述的BaseInputModule的对应方法

        private void TickModules()
        {
            for (var i = 0; i < m_SystemInputModules.Count; i++)
            {
                if (m_SystemInputModules[i] != null)
                    m_SystemInputModules[i].UpdateModule();
            }
        }
        public override void UpdateModule()
        {
            if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
            {
                if (m_InputPointerEvent != null && m_InputPointerEvent.pointerDrag != null && m_InputPointerEvent.dragging)
                {
                    ReleaseMouse(m_InputPointerEvent, m_InputPointerEvent.pointerCurrentRaycast.gameObject);
                }

                m_InputPointerEvent = null;

                return;
            }

            m_LastMousePosition = m_MousePosition;
            m_MousePosition = input.mousePosition;
        }

对于EventSystem的Update方法中间部分则是为了确保该输入模块适应当前平台的保障

在EventSystem的Update方法最后执行了每个BaseInputModule的Process方法

        public override void Process()
        {
            if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
                return;

            bool usedEvent = SendUpdateEventToSelectedObject();

            // case 1004066 - touch / mouse events should be processed before navigation events in case
            // they change the current selected gameobject and the submit button is a touch / mouse button.

            // touch needs to take precedence because of the mouse emulation layer
            if (!ProcessTouchEvents() && input.mousePresent)
                ProcessMouseEvent();

            if (eventSystem.sendNavigationEvents)
            {
                if (!usedEvent)
                    usedEvent |= SendMoveEventToSelectedObject();

                if (!usedEvent)
                    SendSubmitEventToSelectedObject();
            }
        }

其中SendUpdateEventToSelectedObject方法将EventSystem中之前被选中的物体执行Selected事件

之后若鼠标可用将执行处理鼠标事件的方法即ProcessMouseEvent

        protected void ProcessMouseEvent(int id)
        {
            var mouseData = GetMousePointerEventData(id);
            var leftButtonData = mouseData.GetButtonState(PointerEventData.InputButton.Left).eventData;

            m_CurrentFocusedGameObject = leftButtonData.buttonData.pointerCurrentRaycast.gameObject;

            // Process the first mouse button fully
            ProcessMousePress(leftButtonData);
            ProcessMove(leftButtonData.buttonData);
            ProcessDrag(leftButtonData.buttonData);

            // Now process right / middle clicks
            ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData);
            ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Right).eventData.buttonData);
            ProcessMousePress(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData);
            ProcessDrag(mouseData.GetButtonState(PointerEventData.InputButton.Middle).eventData.buttonData);

            if (!Mathf.Approximately(leftButtonData.buttonData.scrollDelta.sqrMagnitude, 0.0f))
            {
                var scrollHandler = ExecuteEvents.GetEventHandler(leftButtonData.buttonData.pointerCurrentRaycast.gameObject);
                ExecuteEvents.ExecuteHierarchy(scrollHandler, leftButtonData.buttonData, ExecuteEvents.scrollHandler);
            }
        }

GetMousePointerEventData方法是在抽象类PointerInputModule中,该方法进行组装鼠标指针数据,处理这一帧与上一帧的鼠标位置信息,以处理之后的拖拽事件,在组装数据时,先是组装了鼠标左键的数据,组装中还调用了EventSystem的RaycastAll方法,该方法得到该鼠标指针位置下所有射线模块能检测到的物体,之后对于鼠标右键与中键的数据是复制了鼠标左键的数据,之后进行返回该鼠标指针数据.

在得到鼠标指针数据之后ProcessMouseEvent进行各种鼠标事件调用,将调用类型与指针数据中的射线检测到的物体传递给ExecuteEvents,在ExecuteEvents中进行各种事件的触发

总结

EventSystem中保存所有输入模块,在EventSystem的Update方法中进行轮询每个输入模块的TickModules与Process方法,其中TickModules方法将EventSystem类中的保存的选中的对象进行触发Selected事件,保存的选中的对象可在Selected类中进行设置,之后在Process方法调用后进行组装鼠标指针数据,射线检测该指针下的物体,最后在ExecuteEvents中进行各种事件调用

如果觉得文章有用请关注B站EOE组合 谢谢喵

你可能感兴趣的:(unity,游戏引擎)