U2L13-2——物理系统之射线检测

本章代码关键字

1
2
3
4
5
6
7
8
9
10
11
12
13
Ray                            //射线类,根据起点和方向确定射线,用于接下来的检测函数
ray.origin //射线的起点
ray.direction //射线的方向
camera.ScreenPointToRay() //相机的屏幕位置转射线,从屏幕的某个点射出射线
Physics.Raycast() //射线检测函数,通过传入的射线(或者给定起点和方向)检测特定层级上是否有对象相交,用out关键字传入RaycastHit,会为该变量赋予物体信息
RaycastHit //射线检测被击中对象的信息结构体,里面有各种信息
raycastHit.collider //射线检测被击中对象的碰撞器,可以通过这个进一步获取信息
raycastHit.point //射线击中的点
raycastHit.normal //射线击中的面的向量
raycastHit.transform //射线检测被击中对象的transform信息,可以得到诸如位置等信息
raycastHit.distance //起点到射线击中的点的距离
Physics.RaycastAll() //射线检测函数,检测所有击中的对象,返回一个RaycastHit数组
Physics.RaycastNonAlloc() //射线检测函数,检测击中的对象的数量

射线检测

物理系统中,目前我们学习的物体相交判断有:

  1. 碰撞检测函数 —— 必备条件:1、刚体,2、碰撞器
  2. 范围检测 —— 必备条件:碰撞器

如果想要做这样的碰撞检测

  1. 鼠标选择场景上一物体
  2. FPS射击游戏(无弹道 - 不产生实际的子弹对象进行移动)
    等等 需要判断一条线和物体的碰撞情况

射线检测 就是来解决这些问题的,它可以在指定点发射一个指定方向的射线
判断该射线与哪些碰撞器相交,得到对应对象

射线对象

3D世界中的射线

注意:单独的射线对于我们来说,没有任何实际的意义,我们需要用它来结合物理系统进行射线判断

假设有一条 起点为坐标(1, 0, 0) 方向为世界坐标Z轴正方向的射线
注意:理解参数含义

  • 参数一:起点
  • 参数二:方向 (一定记住 不是两点决定射线方向,第二个参数直接就代表 方向向量
1
Ray r = new Ray(Vector3.right, Vector3.forward);

目前只是申明了一个射线对象,对于我们来说,没有任何用处

Ray中的参数

1
2
print(r.origin);    //起点
print(r.direction); //方向

摄像机发射出的射线

得到一条从屏幕位置作为起点,以摄像机视口方向为方向的射线

1
Ray r2 = Camera.main.ScreenPointToRay(Input.mousePosition);

射线的碰撞检测函数

Physics类中提供了很多进行射线检测的静态函数
他们有很多种重载类型我们只需要掌握核心的几个函数其它函数自然就明白什么意思了
注意:射线检测也是瞬时的,执行代码时进行一次射线检测

注意!距离、层级两个参数,如果都是int类型,可能会出现混淆问题****所以当我们传入参数时,一定要明确传入的参数代表的是距离还是层级!!! 参数里,距离在前,层级在后!
例如:下面的内容就是错误的

1
2
if (Physics.Raycast(r3, 1 << LayerMask.NameToLayer("Monster"))    //这个参数是错误的!!!
print("碰撞到了对象");

最原始的射线检测

准备一条射线

1
Ray r3 = new Ray(Vector3.zero, Vector3.forward);

进行射线检测,如果碰撞到对象 返回Ture

  • 参数一:射线对象
  • 参数二:检测的最大距离,超过这个距离不检测
  • 参数三:检测指定层级(不填检测所有层)(注意!有特殊的填入规则!
  • 参数四:是否忽略触发器 UseGlobal​-使用全局设置 Collide​-检测触发器 Ignore​-忽略触发器 不填使用UseGlobal
  • 返回值:bool​,当碰撞到对象时,返回true​,否则,返回false
1
2
3
4
if (Physics.Raycast(r3, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal))
{
print("碰撞到了对象");
}

还有一种重载,不用射线,直接传入起点和方向 也可以用于判断
就是把 第一个参数射线 变成了 射线的两个参数:起点和方向

1
2
3
4
if (Physics.Raycast(Vector3.zero, Vector3.forward, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal))
{
print("碰撞到了对象2");
}

但是只有射线检测是不能直接得到被击中的对象的信息的,我们还需要RaycastHit类来装载物体信息

获取相交的单个物体信息

物体信息类 RaycastHit

RaycastHit​ 该类 对于我们的意义
它不仅可以得到我们碰撞到的对象信息
还可以得到一些 碰撞的点 距离 法线 等等的信息
使用这些信息可以用来创建诸如弹痕之类的东西

1
2
3
4
5
6
7
8
9
10
RaycastHit hitInfo;
print(hitInfo.collider.gameObject.name);
//碰撞到的点,可以用于创建诸如弹痕,飙血等特效
print(hitInfo.point);
//法线信息,可以用于确定诸如弹痕,飙血的特效方向
print(hitInfo.normal);
//得到碰撞到对象的位置信息(依附于对象的transform)
print(hitInfo.transform.position);
//可以得到碰撞到对象 离起点的距离,可以用于计算诸如伤害衰减,子弹抛物线等
print(hitInfo.distance);
  • 参数一:射线对象
  • 参数二:RaycastHit结构体 是值类型 Unity会通过 out关键字 在函数内部处理后 得到碰撞数据返回到该参数中
  • 参数三:检测的最大距离,超过这个距离不检测
  • 参数四:检测指定层级(不填检测所有层)(注意!有特殊的填入规则!
  • 参数五:是否忽略触发器 UseGlobal​-使用全局设置 Collide​-检测触发器 Ignore​-忽略触发器 不填使用UseGlobal
1
2
3
4
5
6
7
8
9
10
11
12
13
14
if (Physics.Raycast(r3, out hitInfo, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal))
{
print("碰撞到了对象,得到了信息");
//碰撞器信息,可以获取到该对象的一切信息
print(hitInfo.collider.gameObject.name);
//碰撞到的点,可以用于创建诸如弹痕,飙血等特效
print(hitInfo.point);
//法线信息,可以用于确定诸如弹痕,飙血的特效方向
print(hitInfo.normal);
//得到碰撞到对象的位置信息(依附于对象的transform)
print(hitInfo.transform.position);
//可以得到碰撞到对象 离起点的距离,可以用于计算诸如伤害衰减,子弹抛物线等
print(hitInfo.distance);
}

同样的,还有一种重载,不用射线,直接传入起点和方向 也可以用于判断
就是把 第一个参数射线 变成了 射线的两个参数:起点和方向

1
2
3
4
5
6
7
if (Physics.Raycast(Vector3.zero, 
Vector3.forward,
out hitInfo,
1000,
1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal))
{
}

获取相交的多个物体信息

可以得到碰撞到的多个对象
如果没有就是容量为0的数组

  • 参数一:射线对象
  • 参数二:检测的最大距离,超过这个距离不检测
  • 参数三:检测指定层级(不填检测所有层)(注意!有特殊的填入规则!
  • 参数四:是否忽略触发器 UseGlobal​-使用全局设置 Collide​-检测触发器 Ignore​-忽略触发器 不填使用UseGlobal

注意,数组的顺序是,先进来的在开头,后面的在结尾

1
2
3
4
5
RaycastHit[] hits = Physics.RaycastAll(r3, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal);
for (int i = 0; i < hits.Length; i++)
{
print(hits[i].collider.gameObject.name);
}

同样的,还有一种重载,不用射线,直接传入起点和方向 也可以用于判断
就是把 第一个参数射线 变成了 射线的两个参数:起点和方向

1
2
3
4
5
hits = Physics.RaycastAll(Vector3.zero,
Vector3.forward,
1000,
1 << LayerMask.NameToLayer("Monster"),
QueryTriggerInteraction.UseGlobal);

返回碰撞的数量

返回碰撞的数量,通过out​得到数据

1
2
3
4
5
6
7
if (Physics.RaycastNonAlloc(r3,
hits,
1000,
1 << LayerMask.NameToLayer("Monster"),
QueryTriggerInteraction.UseGlobal) > 0)
{
}