• (四)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.结语

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

  • 相关阅读:
    【Problem】前端项目运行:Module build failed:Error Node Sass does not yet support my current environmen
    element ui源码解析 -- Pagination篇( 副:列表页进入详情页再返回后,如何保持当前页码正确 )
    vue mixins是什么及应用
    bootstrap fileinput实现限制图片上传数量及如何控制分批多次上传
    fileinput 配置项大全,从源码中翻出了很多属性,没那么多时间一一验证,特发出来给大家参考参考
    provide inject应用及和props对比
    element ui源码解析 -- input篇
    vue指令应用--实现输入框常见过滤功能
    vue项目实现导出数据到excel
    组件绑定v-model,实现最大化复用
  • 原文地址:https://www.cnblogs.com/llstart-new0201/p/12636577.html
Copyright © 2020-2023  润新知