美文网首页
UGUI源代码分析

UGUI源代码分析

作者: UnityChan | 来源:发表于2019-07-11 17:33 被阅读0次

心血来潮想研究一下ugui的源代码

于是如下所示

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

namespace UnityEngine.EventSystems
{
    /* EventSystem负责管理调度事件,控制各输入模块、射线投射以及事件动作的执行。UI的事件系统处理用户的交互动作,
     * 通过BaseInput来获取用户输入的信息和状态,通过InputModule处理输入,产生和发送事件,
     * 通过RayCaster判断和选择需要响应交互事件的对象,
     * 最终由ExecuteEvents执行相应的回调,调用EventSystemHandler,
     * 完成交互。 */
    [AddComponentMenu("Event/Event System")]
    public class EventSystem : UIBehaviour
    {     
        private  static List<EventSystem> m_EventSystems = new List<EventSystem>();
        public static EventSystem current
        {
            get { return m_EventSystems.Count > 0 ? m_EventSystems[0] : null; }
            set
            {
                int index = m_EventSystems.IndexOf(value);

                if (index >= 0)
                {
                    m_EventSystems.RemoveAt(index);
                    m_EventSystems.Insert(0, value);
                }
            }
        }                   // 全局单例
        protected EventSystem()
        { }

        private List<BaseInputModule> m_SystemInputModules = new List<BaseInputModule>();       // 处于激活状态(系统)的输入模块列表

        private BaseInputModule m_CurrentInputModule;                       // 当前输入模块,后面的ChangeEventModule()方法就是改变它的值
        public BaseInputModule currentInputModule
        {
            get { return m_CurrentInputModule; }
        }

        [SerializeField]
        [FormerlySerializedAs("m_Selected")]
        private GameObject m_FirstSelected;                     // 首个被选中的对象,后面的StandaloneInputModule就用的它
        public GameObject firstSelectedGameObject 
        {
            get { return m_FirstSelected; }
            set { m_FirstSelected = value; }
        }           // Only one object can be selected at a time. Think: controller-selected button.

        private GameObject m_CurrentSelected;                   // 当前被选中的对象,后面的SetSelectedGameObject()方法就是改变它的值
        public GameObject currentSelectedGameObject
        {
            get { return m_CurrentSelected; }
        }          // m_CurrentSelected也作为后面的执行事件的参数,例如ExecuteEvents.Execute(m_CurrentSelected, ...)



        /* 以下两个参数暴露在inspecter面板上,代码里只get了它们,没有set,应该是要自己在面板控制 */

        /* 
         * 怎么说呢,我也只是一只小恐龙,我也不知道怎么描述这个东西
         * 简单来说它就是一个UI导航功能,主机游戏等运用它比较多
         * 就是可以用手柄或者键盘控制移动我们想要选中的UI
         * 想象一下用手柄或者键盘玩游戏时进入装备栏或者菜单栏的场景嘿嘿~
         */
        [SerializeField]
        private bool m_sendNavigationEvents = true;                 
        public bool sendNavigationEvents
        {
            get { return m_sendNavigationEvents; }
            set { m_sendNavigationEvents = value; }
        }

        /* 
         * unity2018开始默认值已经为10啦,不再是5
         * 拖动阈值,和像素密度有关,如果阈值过小,那么一些高分辨率的设备可能就会检测出问题了
         * 比如你只是点击屏幕,触发点击事件,它可能就认为你是在拖动,触发拖动事件。下面是拖动原理
         * 当前帧与上一帧的位移的绝对值大于一个给定阈值时才会触发拖动事件的流程,若低于该值,停止触摸时则触发点击事件
         */
        [SerializeField]
        private int m_DragThreshold = 5;             
        public int pixelDragThreshold
        {
            get { return m_DragThreshold; }
            set { m_DragThreshold = value; }
        }               


        [Obsolete("lastSelectedGameObject is no longer supported")]
        public GameObject lastSelectedGameObject
        {
            get { return null; }
        }


        private bool m_HasFocus = true;                  // 目前还不知道作用,只有在OnApplicationFocus()里面set过。在StandaloneInputModule调用过
        public bool isFocused
        {
            get { return m_HasFocus; }
        }

        private bool m_SelectionGuard;                   // 选择的保护状态,当选择了一个新的对象时,会先将该值置为true,在完成新旧对象选择状态及事件的执行后,再将该值置为false   
        public bool alreadySelecting
        {
            get { return m_SelectionGuard; }
        }                  // 有点类似于我们平时写的单次开关,SetSelectedGameObject()方法里面具体调用

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

                return m_DummyData;
            }
        }

        /// <summary>
        /// 主要用来更新“处于激活状态的输入模块列表 m_SystemInputModules”
        /// 一般会在输入模块的激活状态改变时(OnEnable或OnDisable)调用
        /// </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);
            }
        }


        /// <summary>
        /// 设置新的选中对象为当前被选中的对象。将前一个选中对象覆盖掉,并且分别调用ExecuteEvents,执行相应的回调函数
        /// </summary>
        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;
        }

        public void SetSelectedGameObject(GameObject selected)
        {
            SetSelectedGameObject(selected, baseEventDataCache);
        }


        /// <summary>
        /// 对两个RaycastResult进行排序的具体逻辑
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        /// <returns></returns>
        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;

        /// <summary>
        /// 功能类,将PointerEventData放进列表List<RaycastResult>并且排好序
        /// </summary>
        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);      // Raycast()将所有的射线投射数据PointerEventData存入射线投射结果RaycastResult中
            }

            raycastResults.Sort(s_RaycastComparer);         // 列表A.Sort(del a)这个方法表示对列表A进行排序,排序的逻辑写到委托 a里面的
        }                                                   // 意思就是按照a的方法对列表A进行冒泡排序

        public bool IsPointerOverGameObject()
        {
            return IsPointerOverGameObject(PointerInputModule.kMouseLeftId);
        }

        public bool IsPointerOverGameObject(int pointerId)
        {
            if (m_CurrentInputModule == null)
                return false;

            return m_CurrentInputModule.IsPointerOverGameObject(pointerId);
        }

        protected override void OnEnable()
        {
            base.OnEnable();
            m_EventSystems.Add(this);
        }

        protected override void OnDisable()
        {
            if (m_CurrentInputModule != null)
            {
                m_CurrentInputModule.DeactivateModule();
                m_CurrentInputModule = null;
            }

            m_EventSystems.Remove(this);

            base.OnDisable();
        }

        private void TickModules()             // 每帧都会调用,遍历当前的所有输入模块,调用其UpdateModule()方法,更新每一个InputModule
        {
            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()
        {
            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())  // 判断这些module是否支持当前的平台且module是否可激活
                {
                    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();        // Process()将各种输入事件(如点击、拖拽等事件)传递给EventSystem当前选中的GameObject(m_CurrentSelected)
        }

        /// <summary>
        /// 此方法就一个功能,将 module 赋值给 m_CurrentInputModule
        /// </summary>
        /// <param name="module"></param>
        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();
        }
    }
}

想说的都在代码注释里面了,花了半天时间,累了,水文结束。

相关文章

网友评论

      本文标题:UGUI源代码分析

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