UGUI源码解析二:EventSystem

1.EventSystem介绍

EventSystem继承自UIBehaviour,场景中可以存在多个EventSystem组件,但是只会有一个生效。每个EventSystem都会在OnEnable时将自身添加到m_EventSystems这个静态变量中,在OnDisable的时候将自身从m_EventSystems移除。每次Update时都会判断current(每次都是m_EventSystems中第一个)是否为自身。

2.EventSystem主要属性/字段

属性/字段名

说明

是否序列化到Inspector面板

List<BaseInputModule> m_SystemInputModules

输入模块列表

BaseInputModule m_CurrentInputModule

当前输入模块

List<EventSystem> m_EventSystems

EventSystem列表

EventSystem current

当前生效的输入模块

GameObject m_FirstSelected

首次选中的对象,运行之前可以指定一个对象,在运行之后立即选中该对象。

bool m_sendNavigationEvents

是否发送导航事件

int m_DragThreshold

拖动事件触发的最小像素,表示鼠标移动多少个像素才能触发拖动事件

GameObject m_CurrentSelected

当前选中的GameObject

GameObject lastSelectedGameObject

上次选中的GameObject

bool m_HasFocus

程序焦点

导航事件:移动(Move),提交(Submit),取消(Cancel)

3.Event System主要作用

EventSystem在UGUI源码中属于事件逻辑处理模块。所有UI事件都是通过EventSystem类中通过轮询检测到并作相应事件执行,通过调用输入事件检测模块和检测碰撞模块来形成自己的主逻辑。

3.1.管理更新输入模块

// 收集当前对象上所有的输入模块, 在BaseInputModule->OnEnable/OnDisable时触发
// 在BaseInputModule可用和不可用时会通过触发此方法来添加或者移除自己
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在生命周期函数Update中触发
// 这里的只是更新数据, 而不进行真正的事件处理, 真正的事件处理只有当前的输入模块才处理, 也就是说同时只有一个输入模块会处理事件
private void TickModules()
{
    for (var i = 0; i < m_SystemInputModules.Count; i++)
    {
        if (m_SystemInputModules[i] != null)
            m_SystemInputModules[i].UpdateModule();
    }
}

// 更新输入模块, 切换当前激活的输入模块并处理事件
protected virtual void Update()
{
    //判断自身是否是生效的EventSystem 同一场景只有一个EventSystem会生效
    if (current != this)
        return;
    TickModules();

    //切换当前激活的InputModule
    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;
        }
    }

    //没有支持当前平台且处于激活状态的InputModule时,将第一个支持当前平台的InputModule激活
    // 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();
}

// 切换当前生效的输入模块
private void ChangeEventModule(BaseInputModule module)
{
    if (m_CurrentInputModule == module)
        return;

    if (m_CurrentInputModule != null)
        m_CurrentInputModule.DeactivateModule();

    if (module != null)
        module.ActivateModule();
    m_CurrentInputModule = module;
}

3.2.管理射线检测模块进行射线检测,比较检测结果的优先级

//当前所有生效的Raycaster进行射线检测,再将检测到的所有对象进行排序(RaycastComparer)
//在PointerInputModule中检测到点击或者触摸时间时触发
public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
{
    raycastResults.Clear();
    var modules = RaycasterManager.GetRaycasters();
    for (int i = 0; i < modules.Count; ++i)
    {
        var module = modules[i];
        if (module == null || !module.IsActive())
            continue;

        module.Raycast(eventData, raycastResults);
    }

    raycastResults.Sort(s_RaycastComparer);
}

///对射线穿透的所有未屏蔽的物体的优先级进行排序
/// 排序规则
/// 1.如果Canvas指定了不同的Camera,则按照Camvas深度进行排序,大的在前
/// 2.按照射线发射器的sortOrderPriority进行排序,大的在前
/// 3.按照射线发射器的renderOrderPriority进行排序,大的在前
/// 4.按照被检测物体的sortingLayer进行排序,大的在前
/// 5.按照被检测物体的sortingOrder进行排序,大的在前
/// 6.按照被检测物体的depth进行排序,在PhysicsRaycaster,Physic2DRaycaster两种射线模式下,该值为0.大的在前
/// 7.按照射线出发点到碰撞位置的距离,距离小的在前
/// 8.按照射线检测结果的索引值(index),小的放在前面。
private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
{
    if (lhs.module != rhs.module)
    {
        var lhsEventCamera = lhs.module.eventCamera;
        var rhsEventCamera = rhs.module.eventCamera;
        if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
        {
            // need to reverse the standard compareTo
            if (lhsEventCamera.depth < rhsEventCamera.depth)
                return 1;
            if (lhsEventCamera.depth == rhsEventCamera.depth)
                return 0;

            return -1;
        }

        if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
            return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

        if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
            return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
    }

    if (lhs.sortingLayer != rhs.sortingLayer)
    {
        // Uses the layer value to properly compare the relative order of the layers.
        var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
        var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
        return rid.CompareTo(lid);
    }


    if (lhs.sortingOrder != rhs.sortingOrder)
        return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

    if (lhs.depth != rhs.depth)
        return rhs.depth.CompareTo(lhs.depth);

    if (lhs.distance != rhs.distance)
        return lhs.distance.CompareTo(rhs.distance);

    return lhs.index.CompareTo(rhs.index);
}

3.3.其他重要接口

//程序聚焦
protected virtual void OnApplicationFocus(bool hasFocus)
{
    m_HasFocus = hasFocus;
}

//设置当前选中对象,通过在不同的组件对象点击回调中设置
public void SetSelectedGameObject(GameObject selected, BaseEventData pointer)
{
    if (m_SelectionGuard)       //避免同一时间设置多个选中对象
    {
        Debug.LogError("Attempting to select " + selected +  "while already selecting an object.");
        return;
    }

    m_SelectionGuard = true;
    if (selected == m_CurrentSelected)      //选中同一对象不执行事件
    {
        m_SelectionGuard = false;
        return;
    }

    // Debug.Log("Selection: new (" + selected + ") old (" + m_CurrentSelected + ")");
    //旧对象发送取消选中事件
    ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.deselectHandler);       //老对象执行取消选中事件
    m_CurrentSelected = selected;
    //新对象发送选中事件
    ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.selectHandler);         //新对象执行选中事件
    m_SelectionGuard = false;
}

Event System源码及注释解析

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Serialization;

namespace UnityEngine.EventSystems
{
    [AddComponentMenu("Event/Event System")]
    /// <summary>
    /// Handles input, raycasting, and sending events.
    /// </summary>
    /// <remarks>
    /// The EventSystem is responsible for processing and handling events in a Unity scene. A scene should only contain one EventSystem. The EventSystem works in conjunction with a number of modules and mostly just holds state and delegates functionality to specific, overrideable components.
    /// When the EventSystem is started it searches for any BaseInputModules attached to the same GameObject and adds them to an internal list. On update each attached module receives an UpdateModules call, where the module can modify internal state. After each module has been Updated the active module has the Process call executed.This is where custom module processing can take place.
    /// </remarks>
    public class EventSystem : UIBehaviour
        {
            private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();           //输入模块列表

            private BaseInputModule m_CurrentInputModule;                                               //当前输入模块

            private  static List<EventSystem> m_EventSystems = new List<EventSystem>();                 //EventSystem列表

            /// <summary>
            /// Return the current EventSystem.
            /// </summary>
            public static EventSystem current                                                           //当前生效的EventSystem
            {
                get { return m_EventSystems.Count > 0 ? m_EventSystems[0] : null; }                     //每次只会取EventSyste列表中第一个元素
                set
                {
                    int index = m_EventSystems.IndexOf(value);

                    if (index >= 0)
                    {
                        m_EventSystems.RemoveAt(index);
                        m_EventSystems.Insert(0, value);
                    }
                }
            }

            [SerializeField]
            [FormerlySerializedAs("m_Selected")]
            private GameObject m_FirstSelected;                                                         //首次选中的对象,运行前可以指定,运行后立即选中该对象

            [SerializeField]
            private bool m_sendNavigationEvents = true;                                                 //是否发送导航事件

            /// <summary>
            /// Should the EventSystem allow navigation events (move / submit / cancel).
            /// </summary>
            public bool sendNavigationEvents
            {
                get { return m_sendNavigationEvents; }
                set { m_sendNavigationEvents = value; }
            }

            [SerializeField]
            private int m_DragThreshold = 10;                                                           //拖动事件触发的最小像素,表示指针至少移动多少像素才能触发拖动事件

            /// <summary>
            /// The soft area for dragging in pixels.
            /// </summary>
            public int pixelDragThreshold
            {
                get { return m_DragThreshold; }
                set { m_DragThreshold = value; }
            }

            private GameObject m_CurrentSelected;                                                       //当前选中的GameObject

            /// <summary>
            /// The currently active EventSystems.BaseInputModule.
            /// </summary>
            public BaseInputModule currentInputModule
            {
                get { return m_CurrentInputModule; }
            }

            /// <summary>
            /// Only one object can be selected at a time. Think: controller-selected button.
            /// </summary>
            public GameObject firstSelectedGameObject
            {
                get { return m_FirstSelected; }
                set { m_FirstSelected = value; }
            }

            /// <summary>
            /// The GameObject currently considered active by the EventSystem.
            /// </summary>
            public GameObject currentSelectedGameObject
            {
                get { return m_CurrentSelected; }
            }

            [Obsolete("lastSelectedGameObject is no longer supported")]
            public GameObject lastSelectedGameObject                                                    //上次选中的GameObject
            {
                get { return null; }
            }

            private bool m_HasFocus = true;                                                             //应用焦点

            /// <summary>
            /// Flag to say whether the EventSystem thinks it should be paused or not based upon focused state.
            /// </summary>
            /// <remarks>
            /// Used to determine inside the individual InputModules if the module should be ticked while the application doesnt have focus.
            /// </remarks>
            public bool isFocused
            {
                get { return m_HasFocus; }
            }

            protected EventSystem()
            {}

        	// 收集当前对象上所有的输入模块, 在BaseInputModule->OnEnable/OnDisable时触发
			// 在BaseInputModule可用和不可用时会通过触发此方法来添加或者移除自己
            /// <summary>
            /// Recalculate the internal list of BaseInputModules.
            /// </summary>
            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);
                }
            }

            private bool m_SelectionGuard;                                                              //避免同一时间设置多个选中对象

            /// <summary>
            /// Returns true if the EventSystem is already in a SetSelectedGameObject.
            /// </summary>
            public bool alreadySelecting
            {
                get { return m_SelectionGuard; }
            }

            //设置当前选中对象,通过在不同的组件对象点击回调中设置
            /// <summary>
            /// Set the object as selected. Will send an OnDeselect the the old selected object and OnSelect to the new selected object.
            /// </summary>
            /// <param name="selected">GameObject to select.</param>
            public void SetSelectedGameObject(GameObject selected, BaseEventData pointer)
            {
                if (m_SelectionGuard)       //避免同一时间设置多个选中对象
                {
                    Debug.LogError("Attempting to select " + selected +  "while already selecting an object.");
                    return;
                }

                m_SelectionGuard = true;
                if (selected == m_CurrentSelected)      //选中同一对象不执行事件
                {
                    m_SelectionGuard = false;
                    return;
                }

                // Debug.Log("Selection: new (" + selected + ") old (" + m_CurrentSelected + ")");
                ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.deselectHandler);       //老对象执行取消选中事件
                m_CurrentSelected = selected;
                ExecuteEvents.Execute(m_CurrentSelected, pointer, ExecuteEvents.selectHandler);         //新对象执行选中事件
                m_SelectionGuard = false;
            }

            private BaseEventData m_DummyData;
            private BaseEventData baseEventDataCache
            {
                get
                {
                    if (m_DummyData == null)
                        m_DummyData = new BaseEventData(this);

                    return m_DummyData;
                }
            }

            /// <summary>
            /// Set the object as selected. Will send an OnDeselect the the old selected object and OnSelect to the new selected object.
            /// </summary>
            /// <param name="selected">GameObject to select.</param>
            public void SetSelectedGameObject(GameObject selected)
            {
                SetSelectedGameObject(selected, baseEventDataCache);
            }

            ///对射线穿透的所有未屏蔽的物体的优先级进行排序
            /// 排序规则
            /// 1.如果Canvas指定了不同的Camera,则按照Camvas深度进行排序,大的在前
            /// 2.按照射线发射器的sortOrderPriority进行排序,大的在前
            /// 3.按照射线发射器的renderOrderPriority进行排序,大的在前
            /// 4.按照被检测物体的sortingLayer进行排序,大的在前
            /// 5.按照被检测物体的sortingOrder进行排序,大的在前
            /// 6.按照被检测物体的depth进行排序,在PhysicsRaycaster,Physic2DRaycaster两种射线模式下,该值为0.大的在前
            /// 7.按照射线出发点到碰撞位置的距离,距离小的在前
            /// 8.按照射线检测结果的索引值(index),小的放在前面。
            private static int RaycastComparer(RaycastResult lhs, RaycastResult rhs)
            {
                if (lhs.module != rhs.module)
                {
                    var lhsEventCamera = lhs.module.eventCamera;
                    var rhsEventCamera = rhs.module.eventCamera;
                    if (lhsEventCamera != null && rhsEventCamera != null && lhsEventCamera.depth != rhsEventCamera.depth)
                    {
                        // need to reverse the standard compareTo
                        if (lhsEventCamera.depth < rhsEventCamera.depth)
                            return 1;
                        if (lhsEventCamera.depth == rhsEventCamera.depth)
                            return 0;

                        return -1;
                    }

                    if (lhs.module.sortOrderPriority != rhs.module.sortOrderPriority)
                        return rhs.module.sortOrderPriority.CompareTo(lhs.module.sortOrderPriority);

                    if (lhs.module.renderOrderPriority != rhs.module.renderOrderPriority)
                        return rhs.module.renderOrderPriority.CompareTo(lhs.module.renderOrderPriority);
                }

                if (lhs.sortingLayer != rhs.sortingLayer)
                {
                    // Uses the layer value to properly compare the relative order of the layers.
                    var rid = SortingLayer.GetLayerValueFromID(rhs.sortingLayer);
                    var lid = SortingLayer.GetLayerValueFromID(lhs.sortingLayer);
                    return rid.CompareTo(lid);
                }


                if (lhs.sortingOrder != rhs.sortingOrder)
                    return rhs.sortingOrder.CompareTo(lhs.sortingOrder);

                if (lhs.depth != rhs.depth)
                    return rhs.depth.CompareTo(lhs.depth);

                if (lhs.distance != rhs.distance)
                    return lhs.distance.CompareTo(rhs.distance);

                return lhs.index.CompareTo(rhs.index);
            }

            private static readonly Comparison<RaycastResult> s_RaycastComparer = RaycastComparer;
            
            //当前所有生效的Raycaster进行射线检测,再将检测到的所有对象进行排序(RaycastComparer)
            //在PointerInputModule中检测到点击或者触摸时间时触发
            /// <summary>
            /// Raycast into the scene using all configured BaseRaycasters.
            /// </summary>
            /// <param name="eventData">Current pointer data.</param>
            /// <param name="raycastResults">List of 'hits' to populate.</param>
            public void RaycastAll(PointerEventData eventData, List<RaycastResult> raycastResults)
            {
                raycastResults.Clear();
                var modules = RaycasterManager.GetRaycasters();
                for (int i = 0; i < modules.Count; ++i)
                {
                    var module = modules[i];
                    if (module == null || !module.IsActive())
                        continue;

                    module.Raycast(eventData, raycastResults);
                }

                raycastResults.Sort(s_RaycastComparer);
            }

            /// <summary>
            /// Is the pointer with the given ID over an EventSystem object?
            /// </summary>
            public bool IsPointerOverGameObject()
            {
                return IsPointerOverGameObject(PointerInputModule.kMouseLeftId);
            }

            /// <summary>
            /// Is the pointer with the given ID over an EventSystem object?
            /// </summary>
            /// <remarks>
            /// If you use IsPointerOverGameObject() without a parameter, it points to the "left mouse button" (pointerId = -1); therefore when you use IsPointerOverGameObject for touch, you should consider passing a pointerId to it
            /// Note that for touch, IsPointerOverGameObject should be used with ''OnMouseDown()'' or ''Input.GetMouseButtonDown(0)'' or ''Input.GetTouch(0).phase == TouchPhase.Began''.
            /// </remarks>
            /// <example>
            /// <code>
            /// using UnityEngine;
            /// using System.Collections;
            /// using UnityEngine.EventSystems;
            ///
            /// public class MouseExample : MonoBehaviour
            /// {
            ///     void Update()
            ///     {
            ///         // Check if the left mouse button was clicked
            ///         if (Input.GetMouseButtonDown(0))
            ///         {
            ///             // Check if the mouse was clicked over a UI element
            ///             if (EventSystem.current.IsPointerOverGameObject())
            ///             {
            ///                 Debug.Log("Clicked on the UI");
            ///             }
            ///         }
            ///     }
            /// }
            /// </code>
            /// </example>
            
            //具有给定ID的指针是否位于EventSystem对象上。一般可以用来判断鼠标是否点击在UI上 
            //参数pointerId 真机时需要传触摸的fingerId  不传时代表鼠标左键
            public bool IsPointerOverGameObject(int pointerId)
            {
                if (m_CurrentInputModule == null)
                    return false;

                return m_CurrentInputModule.IsPointerOverGameObject(pointerId);
            }

            //OnEnable时将自身加到静态EventSystem列表中
            protected override void OnEnable()
            {
                base.OnEnable();
                m_EventSystems.Add(this);
            }

            //OnDisable时将自身从静态EventSystem列表中移除且释放inputModule
            protected override void OnDisable()
            {
                if (m_CurrentInputModule != null)
                {
                    m_CurrentInputModule.DeactivateModule();
                    m_CurrentInputModule = null;
                }

                m_EventSystems.Remove(this);

                base.OnDisable();
            }

            // 触发输入模块的更新, 由当前激活的EventSystem在生命周期函数Update中触发
            // 这里的只是更新数据, 而不进行真正的事件处理, 真正的事件处理只有当前的输入模块才处理, 也就是说同时只有一个输入模块会处理事件
            private void TickModules()
            {
                for (var i = 0; i < m_SystemInputModules.Count; i++)
                {
                    if (m_SystemInputModules[i] != null)
                        m_SystemInputModules[i].UpdateModule();
                }
            }

            protected virtual void OnApplicationFocus(bool hasFocus)
            {
                m_HasFocus = hasFocus;
            }

            // 更新输入模块, 切换当前激活的输入模块并处理事件
            protected virtual void Update()
            {
                //判断自身是否是生效的EventSystem 同一场景只有一个EventSystem会生效
                if (current != this)
                    return;
                TickModules();

                //切换当前激活的InputModule
                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;
                    }
                }

                //没有支持当前平台且处于激活状态的InputModule时,将第一个支持当前平台的InputModule激活
                // 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();
            }

            // 切换当前生效的输入模块
            private void ChangeEventModule(BaseInputModule module)
            {
                if (m_CurrentInputModule == module)
                    return;

                if (m_CurrentInputModule != null)
                    m_CurrentInputModule.DeactivateModule();

                if (module != null)
                    module.ActivateModule();
                m_CurrentInputModule = module;
            }

            public override string ToString()
            {
                var sb = new StringBuilder();
                sb.AppendLine("<b>Selected:</b>" + currentSelectedGameObject);
                sb.AppendLine();
                sb.AppendLine();
                sb.AppendLine(m_CurrentInputModule != null ? m_CurrentInputModule.ToString() : "No module");
                return sb.ToString();
            }
        }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值