Unity实用功能之射线检测详解

4,309 阅读3分钟

这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战

概述

射线检测在Unity中属于最常见的功能之一,其作用也是非常大的。在unity中射线检测的应用范围非常的广泛, 多用于碰撞检测比如:在游戏中,开枪射击时,需要判断击中的物体,子弹击中的位置,还有的时候要用到鼠标来控制物体的移动或是鼠标拾取某个物体等等,这些都需要Physics.Raycast和ray。可见他们的重要性。接下来就一起来看一下在Unity中射线检测是如何使用的。

射线

射线,顾名思义,在数学中射线是指由线段的一端无限延长所形成的直的线。射线仅有一个端点,无法测量长度(它无限长)。
在unity中,射线检测,就是从一个固定的点,沿着一个特定的方向,发射一条射线,当和物体发生碰撞后,返回碰撞信息。

详解及用法

在官方API中,我们会发现Ray是一个结构体()

image.png

image.png 接下来看一下ray的用法

 public Ray(Vector3 origin, Vector3 direction);
  • Origin: 射线的起点
  • Direction:射线的方向 以上是官方API中的一部分内容,但是Ray不可能只有这么点功能,其实还有许多应用,别比如说,直接从相机发射射线,射线检测返回碰撞信息等。
  1. Ray Camera.main.ScreenPointToRay(Vector3 pos)   返回一条射线Ray从摄像机到屏幕指定一个点
  2. Ray Camera.main.ViewportPointToRay(Vector3 pos)  返回一条射线Ray从摄像机到视口(视口之外无效)指定一个点
  3. RaycastHit 射线检测返回射线的碰撞信息
  4. bool Physics.Raycast 射线是否与物体发生的碰撞器发生碰撞,发生碰撞为true
  5. RaycastHit[] RaycastAll 射线检测并返回所有碰撞,也就是射线检测并返回一个RaycastHit[]结构体。

Camera.main.ScreenPointToRayCamera.main.ViewportPointToRay
这两个射线检测跟好理解,直接就是以摄像机为起始,然后发射一条射线,其中的参数Vector3 pos就是射线要发射的方向。 一般常用的是向鼠标点击的位置发射一条射线,多用于射击等游戏

 Camera.main.ScreenPointToRay(Input.mousePosition);//从摄像机发出一条射线,到点击的坐标

RaycastHitbool Physics.Raycast
在项目中,只发射一条射线还不行,发射射线了,我们还要知道,射线碰撞到了什么物体,所以就用到了Physics.Raycast

image.png 一般常用的为前三个,让我们一起来看一下其中的参数

  • Ray ray:我们上面创建射线
  • out RaycastHit hitInfo:返回的碰撞信息(可以得到transform,rigidbody,point等信息。)
  • float maxDistance:射线的最大长度是多长
  • int layerMask:只选定Layermask层内的碰撞器,其它层内碰撞器忽略。 选择性的碰撞 其中int layerMask参数还是比较有意思的,让我们一起来看一下。
    layerMask就是设置一下射线检测的层级,在Unity中每个GameObject都会有一个Layer,这里的检测层级就是指检测Layer中的某一层或是某几层,其他的物体Layer不是设置的要检测的层,将不会被射线检测,射线将会直接穿透过去。
    layerMask其实很简单,大概分为四种表达方式:
  • 1 << 10  只检测第10的层。
  • ~(1 << 10) 检测除了第10之外的层。
  • ~(1 << 0) 检测所有的层(也可以不填参数,就默认检测所有)。
  • (1 << 10) | (1 << 8) 检测第10和第8的层。

接下来看一个例子: 通过鼠标点击,从相机发射一条射线,射线只检测我们自定义的Layer8:Cube,并打印射线检测到的物体名称

展示3.gif 通过上图我们可以看到,只有一个Cube物体被检测到,那是因为只有Cube的层级我们设置为了8。细心的朋友会发现在Scene中还有红色线条的出现,那是Debug.DrawLine画出来的

DrawLine
Debug.DrawLine是untiy中在Scene视图花线条的方法,常用的方法如下,此方法是画出一条射线多配合射线检测Debugshiyong ,此功能仅能用在Scene视图中

public static void DrawLine(Vector3 start, Vector3 end, Color color, float duration);
  • start:线条的起始点
  • end:线条的终点
  • color:线条的颜色
  • duration:线条存在的时间

源码

上述Demo的源码

private RaycastHit objhit;
private Ray _ray;
private int layerMask = 1 << 8;
   
void Update()
{
    if (Input.GetMouseButtonDown(0))
    {
        _ray = Camera.main.ScreenPointToRay(Input.mousePosition);//从摄像机发出一条射线,到点击的坐标
        if (Physics.Raycast(_ray, out objhit, 1000, layerMask))
        {
            Debug.DrawLine(_ray.origin, objhit.point, Color.red, 2);//显示一条射线,只有在scene视图中才能看到
            GameObject gameObj = objhit.collider.gameObject;
            Debug.Log("Hit objname:" + gameObj.name + ">>>>Hit objlayer:" + gameObj.layer);
        }
    }
}

写在最后

所有分享的内容均为作者在日常开发过程中使用过的各种小功能点,分享出来也变相的回顾一下,如有写的不好的地方还请多多指教。欢迎大家相互学习进步。