(四)UGUI源码分析之Raycaster

BaseRaycaster 派生出 GraphicRaycaster 和 Physics2DRaycaster、PhysicsRaycaster


GraphicRaycaster 依赖Canvas ,会根据Canvas的sortingOrder和renderOrder以及 canvas.sortingLayerID, canvas.sortingOrder
作为针对RaycastResult的排序属性。
Physics2DRaycaster 和 PhysicsRaycaster依赖于Camera,会根据camera的depth 以及SpriteRenderer的sortingLayerID、
sortingOrder作为排序属性 作为针对RaycastResult的排序属性。

共同拥有的排序属性:hit.distance(碰撞点距离)、索引值


三个派生类都实现了Raycast方法,具体如下解释:
三个方法都是调用Physics类的一些Raycast接口有2D、3D的检测接口,将检测到的物体信息包装为RaycastResult 存入一个列表 并返回给外部使用


PhysicsRaycaster的Raycast方法:
1、从摄像机向鼠标点发送一条射线,获取射线和射线到碰撞点的距离
2、若是3D情况,调用ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, finalEventMask);
第一个参数是射线,第二个参数是射线到碰撞点的距离,第三个是事件遮罩(用于过滤的)
该方法会用反射方式进行调用Physics.RaycastAll()方法,这样获取到了一个RaycastHit列表
还有另一种射线检测情况:ReflectionMethodsCache.Singleton.getRaycastNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
一样是反射进行调用Physics.RaycastNonAlloc方法,这样获取到了一个RaycastHit列表。存入m_Hits参数。
3、排序RaycastHit列表,根据物体距离排序(近 -> 远)
4、对RaycastHit做一个封装,变成RaycastResult存入一个列表返回给外部

Physics2DRaycaster的Raycast方法:
1、从摄像机向鼠标点发送一条射线,获取射线和射线到碰撞点的距离
2、2D情况有两种方式射线检测: 
①m_Hits = ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, finalEventMask);
反射调用 Physics2D.GetRayIntersectionAll方法 进行获取RaycastHit列表
②  hitCount = ReflectionMethodsCache.Singleton.getRayIntersectionAllNonAlloc(ray, m_Hits, distanceToClipPlane, finalEventMask);
反射调用 Physics2D.GetRayIntersectionNonAlloc方法获取RaycastHit列表存入m_Hits返回的是个数
3、将RaycastHit封装成RaycastResult,与PhysicsRaycaster不同的是,多了2个属性即SpriteRenderer的sortingLayer和sortingOrder 用于对RaycastResult排序的属性,然后返回外部使用

GraphicRaycaster的Raycast方法:
1、获取身上Canvas组件关联的已注册Graphic图形列表
2、获取事件摄像机,canvas.renderMode 等于 RenderMode.ScreenSpaceOverlay 时 事件摄像机为空, 
为RenderMode.ScreenSpaceCamera时,摄像机可以被指定,若没有被指定则为空;
为RenderMode.WorldSpace时,摄像机为Camera.main (主摄像机)
3、从事件摄像机身上获取targetDisplay(目标显示屏幕ID),若没有事件摄像机则从Canvas获取targetDisplay
4、Display.RelativeMouseAt(eventData.position)将鼠标点击到的屏幕坐标点转为相对于鼠标的位置名为:eventPosition, 若相对位置不等于(0,0)则判断,它的z值和targetDisplay是否一致,若不一样则return出去。不进行GraphicRaycaster的Raycast方法了;(即代表确保用户点击的屏幕界面ID和摄像机/相关canvas的targetDisplayID要一样,点击的是摄像机/canvas能处理的屏幕界面才继续处理)

public static Vector3 RelativeMouseAt(Vector3 inputMouseCoordinates);

查询鼠标的相对坐标。

RelativeMouseAt可用于查询相对的鼠标输入坐标和记录鼠标输入的屏幕。x, y返回相对空间中的坐标,z返回处理鼠标输入的屏幕,返回Vector3。当不支持多个显示器时为零。目前实现的Windows和macOS的独立播放器。

如果eventPosition(相对位置)等于(0,0) 那么设置这个eventPosition(相对位置)为鼠标点击屏幕坐标位置。
5、支持多屏幕的坐标转换,将eventPosition相对位置(屏幕坐标)转换到视角坐标,然后判断这个视角坐标是否在屏幕内,若在屏幕内继续进行方法,否则return出去  
6、创建一个空射线ray = new Ray(); 
7、当事件摄像机不为空时,从摄像机往eventPosition相对位置坐标点发射一条射线,我们拿到这条射线存入ray。
8、当满足canvas.renderMode != RenderMode.ScreenSpaceOverlay && blockingObjects != BlockingObjects.None)时
进行如下操作:
   8.1 当事件摄像机不为空时,计算出 distanceToClipPlane = 摄像机远近界面距离/ray.direction.z ,当射线z非常接近0时,这个值为无穷大
   8.2 if (blockingObjects == BlockingObjects.ThreeD || blockingObjects == BlockingObjects.All)
        进行 ReflectionMethodsCache.Singleton.raycast3DAll(ray, distanceToClipPlane, (int)m_BlockingMask);
         反射调用Physics.RaycastAll方法 获取射线碰到的所有物体,然后获取最近的第一个物体的距离hitDIstance
   8.3   if (blockingObjects == BlockingObjects.TwoD || blockingObjects == BlockingObjects.All)
        进行调用ReflectionMethodsCache.Singleton.getRayIntersectionAll(ray, distanceToClipPlane, (int)m_BlockingMask);
       反射调用Physics2D.GetRayIntersectionAll方法 获取碰撞到的所有物体,然后获取最近的一个物体的距离hitDIstance
9,    Raycast(canvas, currentEventCamera, eventPosition, canvasGraphics, m_RaycastResults);
   传入了canvas , 当前事件摄像机, 鼠标点击位置, 上面获取的canvas关联的Graphic图形列表, 一个空的RaycastResult列表
    9.1 方法内遍历Graphic图形列表,
         排除depth == -1(这样被认为是图形不可见),
         排除raycastTarget为false的(无法进行被射线检测的被排除),
         排除graphic.canvasRenderer.cull为true的(意味着被裁剪的会排除掉)。
         调用RectTransformUtility.RectangleContainsScreenPoint(graphic.rectTransform, pointerPosition, eventCamera)方法
         方法会传入graphic.rectTransform(RectTransform),pointerPosition位置信息,事件摄像机,返回true代表位置信息处于这个图形的区域内,否则返回false,当返回false时会被排除掉。
         调用事件摄像机的世界转屏幕坐标点方法,将图形的世界位置转屏幕坐标,判断其屏幕坐标Z值是否大于事件摄像机的远截面,若大于则排除掉。(表示图形所处的深度位置已经不在摄像机视角里了)
         调用Graphic的Raycast方法,传入鼠标点击位置和摄像机,进行射线检测若检测到了返回true,否则返回false
         如果检测到了,会将该graphic存入一个列表s_SortedGraphics
     经过上面的遍历后拿到了一个射线检测到的图形列表(经过了一堆过滤和最后的检测)。
    9.2 对这个列表进行排序,根据Graphic.depth深度值来进行倒序
    9.3 将这个列表存入m_RaycastResults 返回给外部使用
10  开始遍历上面获取到的m_RaycastResults列表(射线检测出的Graphic列表)
    10.1 判断是否需要忽略背面图形检测,①若需要忽略,则判断摄像机是否为空,为空则直接默认用Vector3.forward作为摄像机朝向,将摄像机朝向与图形朝向进行点积运算,结果>0代表需要考虑封装入RaycastResult,否则不考虑封装。
       若摄像机不为空则将摄像机真实朝向和图形朝向点积>0则封装 (点积操作>0表示图形正面朝向摄像机,反之是背面朝向)
       ②若不需要忽略,则直接考虑封装入RaycastResult
11  封装RaycastResult过程:(内部也有过滤)
       11.1 如果摄像机为空 或 canvas.renderMode == RenderMode.ScreenSpaceOverlay 则将distance=0
        若摄像机不为空,通过这样的一个运算:
    distance = (Vector3.Dot(transForward, trans.position - ray.origin) / Vector3.Dot(transForward, ray.direction));
         图形朝向和射线原点到图形朝向的点积 除以 图形朝向和射线朝向的点积
      若distance<0,说明图形在摄像机背后则不进行封装。
      如果distance >= hitDistance(在8时通过Physics射线检测得到的一个碰撞点距离),则不进行封装
     否则进行封装成为RaycastResult, 里面的depth代表图形depth,同样和Physics2DRaycaster一样拥有canvas的sortinglayer和
sortingOrder。
 将RaycastResult存入列表中 返回外部使用


至此三个Raycaster的Raycast方法解释完毕,目的是根据点击信息获取到一个RaycastResult列表,给外部使用。
具体用在输入模块调用EventSystem.RaycastAll时,这个方法会调用所有已存在的Raycaster类的Raycast方法将它们检测出的
所有RaycastResult存入一个列表,返回给输入模块,输入模块会获取第一个RaycastResult作为目标进行其他操作。

EventSystem是通过RaycasterManager来获取现存已激活的Raycaster的,每一个Raycaster的OnEnable时会将自身注入RaycasterManager,OnDisable()时会注销自身。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值