UGUI Raycaster

Posted llstart-new0201

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UGUI Raycaster相关的知识,希望对你有一定的参考价值。

1.前言

此文来分析以下Raycaster。虽然Unity的Raycaster等一些组件跟ui放在一起,但是很多属于事件系统。

2.使用位置

在事件系统中,Raycaster用来获取获取当前交互位置(如鼠标位置)对应的游戏物体,其使用位置在EventSystem中的RaycastAll方法。而RaycasterAll方法却是InputModule调用的。

3.射线检测系统

3.1 基本流程

所有的Caycaster都继承子BaseRayster,启动时自动加入到RaycasterManager中,然后由EventSystem调用。目前存在PhysicRaycaster和Physics2DRaycaster以及GraphicRaycaster,分别为3D、2D和ui检测的Raycaster。2d和3D的Raycaster基本相同,差异在于检测时调用raycast,检测的对象不同。基本流程如下所示:
技术图片

3.2 PhysicRaycaster

此节进行PhysicRaycater和Physic2DRaycaster的分析,因为两者均为“三维实体”,有别于UI元素。流程如下:
1)生成射线检测的ray以及计算在camera裁剪平面内的射线距离,以此来舍弃camera空间外的物体。
2)其次进行射线检测,但是射线检测时与我们常规使用Physics.Raycast方法不同,采用的是反射的方法。此部分涉及到变量m_MaxRayIntersections,此变量表示是否由射线检测的结果的数量限制。正常使用都是只返回一个结果,但是此处是返回多个结果,然后进行深度判断。
3)根据深度对结果进行排序,然后将结果添加到RaycasterResult列表中。

3.3 GraphicRaycaster

GraphicRaycaster用来进行ui检测,虽然也使用到了射线,但其当前ui并不是通过射线检测出来的,射线检测只是为了进行2D和3D物体的遮挡距离计算用的。流程如下:
1)获取canvas对应的Graphic,因为UI图像显示的核心是Graphic类,而Graphic类都注册在了CanvasRegistry中,所以通过CanvasRegistry获取所有的Canvas。
2)通过EventData的屏幕坐标,处理多屏幕的问题,在移动端是不存在此问题的。
3)通过射线检测,找到距离camera最近的2d或者3d物体,并计算距离,用来判断ui是否被遮挡。
4)通过判断点击位置是否在ui的rect范围内来确定那个ui是点击ui,并通过计算三角形法则计算点击距离。然后用3)中计算的距离计算遮挡。如果canvas是overlay,则其始终在最前方,所以不存在遮挡问题。
5)生成RaycastReuslt列表。

3.4 射线检测方法

此处进行对使用反射进行射线检测的方法进行说明,代码中通过注释说明是为了避免模块之间的强引用关系(即不需要引入相关模块的使用),所以通过反射方法来获取。也正因为如此,可以直接拷贝出来使用。
代码如下:

using System;
using System.Collections.Generic;
using System.Reflection;

namespace UnityEngine.UI
{
    internal class ReflectionMethodsCache
    {
        public delegate bool Raycast3DCallback(Ray r, out RaycastHit hit, float f, int i);
        public delegate RaycastHit2D Raycast2DCallback(Vector2 p1, Vector2 p2, float f, int i);
        public delegate RaycastHit[] RaycastAllCallback(Ray r, float f, int i);
        public delegate RaycastHit2D[] GetRayIntersectionAllCallback(Ray r, float f, int i);
        public delegate int GetRayIntersectionAllNonAllocCallback(Ray r, RaycastHit2D[] results, float f, int i);
        public delegate int GetRaycastNonAllocCallback(Ray r, RaycastHit[] results, float f, int i);

        // We call Physics.Raycast and Physics2D.Raycast through reflection to avoid creating a hard dependency from
        // this class to the Physics/Physics2D modules, which would otherwise make it impossible to make content with UI
        // without force-including both modules.
        public ReflectionMethodsCache()
        {
            var raycast3DMethodInfo = typeof(Physics).GetMethod("Raycast", new[] {typeof(Ray), typeof(RaycastHit).MakeByRefType(), typeof(float), typeof(int)});
            if (raycast3DMethodInfo != null)
                raycast3D = (Raycast3DCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(Raycast3DCallback), raycast3DMethodInfo);

            var raycast2DMethodInfo = typeof(Physics2D).GetMethod("Raycast", new[] {typeof(Vector2), typeof(Vector2), typeof(float), typeof(int)});
            if (raycast2DMethodInfo != null)
                raycast2D = (Raycast2DCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(Raycast2DCallback), raycast2DMethodInfo);

            var raycastAllMethodInfo = typeof(Physics).GetMethod("RaycastAll", new[] {typeof(Ray), typeof(float), typeof(int)});
            if (raycastAllMethodInfo != null)
                raycast3DAll = (RaycastAllCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(RaycastAllCallback), raycastAllMethodInfo);

            var getRayIntersectionAllMethodInfo = typeof(Physics2D).GetMethod("GetRayIntersectionAll", new[] {typeof(Ray), typeof(float), typeof(int)});
            if (getRayIntersectionAllMethodInfo != null)
                getRayIntersectionAll = (GetRayIntersectionAllCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(GetRayIntersectionAllCallback), getRayIntersectionAllMethodInfo);

            var getRayIntersectionAllNonAllocMethodInfo = typeof(Physics2D).GetMethod("GetRayIntersectionNonAlloc", new[] { typeof(Ray), typeof(RaycastHit2D[]), typeof(float), typeof(int) });
            if (getRayIntersectionAllNonAllocMethodInfo != null)
                getRayIntersectionAllNonAlloc = (GetRayIntersectionAllNonAllocCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(GetRayIntersectionAllNonAllocCallback), getRayIntersectionAllNonAllocMethodInfo);

            var getRaycastAllNonAllocMethodInfo = typeof(Physics).GetMethod("RaycastNonAlloc", new[] { typeof(Ray), typeof(RaycastHit[]), typeof(float), typeof(int) });
            if (getRaycastAllNonAllocMethodInfo != null)
                getRaycastNonAlloc = (GetRaycastNonAllocCallback)UnityEngineInternal.ScriptingUtils.CreateDelegate(typeof(GetRaycastNonAllocCallback), getRaycastAllNonAllocMethodInfo);
        }

        public Raycast3DCallback raycast3D = null;
        public RaycastAllCallback raycast3DAll = null;
        public Raycast2DCallback raycast2D = null;
        public GetRayIntersectionAllCallback getRayIntersectionAll = null;
        public GetRayIntersectionAllNonAllocCallback getRayIntersectionAllNonAlloc = null;
        public GetRaycastNonAllocCallback getRaycastNonAlloc = null;

        private static ReflectionMethodsCache s_ReflectionMethodsCache = null;

        public static ReflectionMethodsCache Singleton
        {
            get
            {
                if (s_ReflectionMethodsCache == null)
                    s_ReflectionMethodsCache = new ReflectionMethodsCache();
                return s_ReflectionMethodsCache;
            }
        }
    };
}

使用时直接调用如下六个回调即可。

        //获取射线检测到的第一个3D物体
        public Raycast3DCallback raycast3D = null;
        //返回检测到的所有3D物体
        public RaycastAllCallback raycast3DAll = null;
        //获取射线检测到的第一个2D物体
        public Raycast2DCallback raycast2D = null;
        //返回检测到的所有2D游戏物体
        public GetRayIntersectionAllCallback getRayIntersectionAll = null;
        //返回检测到的所需2D游戏物体,即返回前n个结果,n可以自己定义
        public GetRayIntersectionAllNonAllocCallback getRayIntersectionAllNonAlloc = null;
        //返回检测到的所需3D游戏物体,即返回前n个结果,n可以自己定义
        public GetRaycastNonAllocCallback getRaycastNonAlloc = null;

4.结语

以上为射线检测的基本分析。

以上是关于UGUI Raycaster的主要内容,如果未能解决你的问题,请参考以下文章

UGUI 源码解读-EventSystem

UGUI 源码解读-EventSystem

UGUI 源码解读-EventSystem

UGUI 源码解读-EventSystem

raycaster选取捕获obj模型&&选中高亮代码

Three.js raycaster 到底在做啥?