UGUI事件和UI渲染

UGUI 事件

场景必须存在EventSystem 并且挂载继承自PointerInputModule基(PointerInputModule继承自BaseInputModule),包括StandaloneInputModule和TouchInputModule。

事件触发从输入类BaseInput开始,此类是对Input类的包装,简化事件以适应UI事件。此类将在StandaloneInputModule和TouchInputModule的基类的基类BaseInputModule的input动态添加初始化。

 public BaseInput input
        {
            get
            {
                if (m_InputOverride != null)
                    return m_InputOverride;

                if (m_DefaultInput == null)
                {
                    var inputs = GetComponents<BaseInput>();
                    foreach (var baseInput in inputs)
                    {
                        // We dont want to use any classes that derrive from BaseInput for default.
                        if (baseInput != null && baseInput.GetType() == typeof(BaseInput))
                        {
                            m_DefaultInput = baseInput;
                            break;
                        }
                    }

                    if (m_DefaultInput == null)
                        m_DefaultInput = gameObject.AddComponent<BaseInput>();
                }

                return m_DefaultInput;
            }
        }

如果挂载EventSystem组件的物体同时存在StandaloneInputModule和TouchInputModule,那么StandaloneInputModule和TouchInputModule分别持有各自的输入类BaseInput。

 

EventSystem 的Update调用StandaloneInputModule和TouchInputModule的UpdateModule()方法实时设置当前点个最后点和Process()。

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

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

StandaloneInputModule和TouchInputModule的UpdateModule()的Process()代码段将会分别调用  以下方法

StandaloneInputModule类调用的方法

  public override void Process()
        {
            if (!eventSystem.isFocused && ShouldIgnoreEventsOnNoFocus())
                return;
            //
            bool usedEvent = SendUpdateEventToSelectedObject();          //SendUpdateEventToSelectedObject() 发送选择事件,并设置当前选择的物体

            if (eventSystem.sendNavigationEvents)
            {
                if (!usedEvent)
                    usedEvent |= SendMoveEventToSelectedObject();       //SendMoveEventToSelectedObject() 发送移动事件,并返回是否移动

                if (!usedEvent)
                    SendSubmitEventToSelectedObject();                  //SendSubmitEventToSelectedObject() 发送提交事件, 
            }

            // touch needs to take precedence because of the mouse emulation layer
            if (!ProcessTouchEvents() && input.mousePresent)            //ProcessTouchEvents() 发送点触事件
                ProcessMouseEvent();                                    //ProcessMouseEvent()  发送点击事件
        }

TouchInputModule类调用的方法:

if (UseFakeInput()) 
    FakeTouches(); 
else               
    ProcessTouchEvents();

最终将会调用StandaloneInputModule和TouchInputModule的基类PointerInputModule的 GetTouchPointerEventData()和GetMousePointerEventData()方法。然后这两个方法将会调用他们持有的EventSystem 的RaycastAll()方法。调用如下:

 eventSystem.RaycastAll(pointerData, m_RaycastResultCache);

RaycastAll()方法将发射一条屏幕射线,获取第一个继承自Graphic基类的组件(如:Image)

RaycastAll()方法将会调用所有继承自BaseRaycaster基类的派生类(PhysicsRaycaster,GraphicRaycaster,Physics2DRaycaster)的Raycast()方法,获取射线所穿过的组件的第一个组件。如GraphicRaycaster,将会获取射线所穿过的所有组件的第一个继承自Graphic的组件 。

代码如下:

   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 (eventCamera != null && eventCamera.WorldToScreenPoint(graphic.rectTransform.position).z > eventCamera.farClipPlane)
                    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();
        }
    }

BaseRaycaster的Raycast()方法又会调用Graphic的Raycast()方法来获取继承自Graphic基类的组件是否被射线穿过。

     public virtual bool Raycast(Vector2 sp, Camera eventCamera)
        {
            if (!isActiveAndEnabled)
                return false;

            var t = transform;
            var components = ListPool<Component>.Get();

            bool ignoreParentGroups = false;
            bool continueTraversal = true;

            while (t != null)
            {
                t.GetComponents(components);
                for (var i = 0; i < components.Count; i++)
                {
                    var canvas = components[i] as Canvas;
                    if (canvas != null && canvas.overrideSorting)
                        continueTraversal = false;

                    var filter = components[i] as ICanvasRaycastFilter;

                    if (filter == null)
                        continue;

                    var raycastValid = true;

                    var group = components[i] as CanvasGroup;
                    if (group != null)
                    {
                        if (ignoreParentGroups == false && group.ignoreParentGroups)
                        {
                            ignoreParentGroups = true;
                            raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);
                        }
                        else if (!ignoreParentGroups)
                            raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);
                    }
                    else
                    {
                        raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);
                    }

                    if (!raycastValid)
                    {
                        ListPool<Component>.Release(components);
                        return false;
                    }
                }
                t = continueTraversal ? t.parent : null;
            }
            ListPool<Component>.Release(components);
            return true;
        }

其中

filter = components[i] as ICanvasRaycastFilter;

raycastValid = filter.IsRaycastLocationValid(sp, eventCamera);

ICanvasRaycastFilter接口(实现此接口的派生类:Mask,RectMask2D,Image)的IsRaycastLocationValid()方法是判断组件是否在渲染范围内,代码如下,

Mask,RectMask2D代码相同:

  public virtual bool IsRaycastLocationValid(Vector2 sp, Camera eventCamera)
        {
            if (!isActiveAndEnabled)
                return true;

            return RectTransformUtility.RectangleContainsScreenPoint(rectTransform, sp, eventCamera);
        }

Image判断相对复杂一些,要判断图片alpha通道,如果alpha通道低于一个值,将不加入射线检测。

  public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
        {
            if (alphaHitTestMinimumThreshold <= 0)
                return true;

            if (alphaHitTestMinimumThreshold > 1)
                return false;

            if (activeSprite == null)
                return true;

            Vector2 local;
            if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local))
                return false;

            Rect rect = GetPixelAdjustedRect();

            // Convert to have lower left corner as reference point.
            local.x += rectTransform.pivot.x * rect.width;
            local.y += rectTransform.pivot.y * rect.height;

            local = MapCoordinate(local, rect);

            // Normalize local coordinates.
            Rect spriteRect = activeSprite.textureRect;
            Vector2 normalized = new Vector2(local.x / spriteRect.width, local.y / spriteRect.height);

            // Convert to texture space.
            float x = Mathf.Lerp(spriteRect.x, spriteRect.xMax, normalized.x) / activeSprite.texture.width;
            float y = Mathf.Lerp(spriteRect.y, spriteRect.yMax, normalized.y) / activeSprite.texture.height;

            try
            {
                return activeSprite.texture.GetPixelBilinear(x, y).a >= alphaHitTestMinimumThreshold;
            }
            catch (UnityException e)
            {
                Debug.LogError("Using alphaHitTestMinimumThreshold greater than 0 on Image whose sprite texture cannot be read. " + e.Message + " Also make sure to disable sprite packing for this sprite.", this);
                return true;
            }
        }

所以上面的条件满足后还必须满足以下条件

所有的UGUI渲染组件(继承自graphic,如Button,Image)最终的父节点必须挂有Canvas组件和继承自射线管理类BaseRaycaster(GraphicRaycaster,PhysicsRaycaster和Physics2DRaycaster)的组件。

BaseRaycaster类将会通过方法OnEnable()调用RaycasterManager.AddRaycaster(this),将所有射线管理类BaseRaycaster注册到射线管理器RaycasterManager中统一管理。

 

RaycasterManager.GetRaycasters();  //获取所有的射线管理类

UGUI渲染组件有一个管理器GraphicRegistry。当继承自Graphic的ui渲染组件被实例化后将把这些渲染组件以此UI渲染组件所属的Canvas为key注册到管理器GraphicRegistry的词典中。词典定义如下:

        private readonly Dictionary<Canvas, IndexedSet<Graphic>> m_Graphics = new Dictionary<Canvas, IndexedSet<Graphic>>();

从词典定义也会发现,每个键Canvas所对应的值Graphic列表正是每个Canvas所管理的渲染的各个UI渲染组件。

实事就是这样。

Graphic管理器 GraphicRegistry有一个方法,通过Canvas获取Canvas下所管理的所有UI渲染组件,方法如下,后面说明将会用到

 public static IList<Graphic> GetGraphicsForCanvas(Canvas canvas)

GraphicRegistry.GetGraphicsForCanvas(canvas);    //获取指定Canvas下的所有继承自Graphic基类的组件

     eventSystem.RaycastAll(pointerData, m_RaycastResultCache); //获取指定屏幕事件的下射线穿过的所有继承自Graphic基类的所有组件。

  var raycast = FindFirstRaycast(m_RaycastResultCache);  //获取射线第一个穿过的有效组件

 

       UGUI的渲染待续

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值