2
-
射线(Ray):
Ray
结构体包含两个主要部分:一个起点(origin
)和一个方向(direction
)。
-
射线检测(Raycast):
Physics.Raycast
方法用于执行射线检测。- 参数包括:
- 射线(
Ray
或起点和方向向量)。 - 检测的最大距离。
- 检测指定层级(使用LayerMask)。
- 是否忽略触发器(
QueryTriggerInteraction
)。
- 射线(
-
LayerMask:
- 用于指定射线检测应该考虑哪些层级的物体。
LayerMask.NameToLayer
方法将层级的名称转换为对应的层级索引。
-
QueryTriggerInteraction:
- 控制射线检测是否应该与触发器(Collider带有Is Trigger属性的)发生交互。
- 选项包括:
UseGlobal
:使用全局设置(Physics.queriesHitTriggers
)。Collide
:总是与触发器交互。Ignore
:忽略触发器。
-
重载方法:
Physics.Raycast
有多个重载版本,允许不同的参数传递方式。
代码示例分析:
// 1. 最原始的射线检测
// 准备一条射线
Ray r3 = new Ray(Vector3.zero, Vector3.forward);
// 进行射线检测 如果碰撞到对象 返回true
// 参数一:射线
// 参数二: 检测的最大距离 超出这个距离不检测
// 参数三:检测指定层级(不填检测所有层)
// 参数四:是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
// 返回值:bool 当碰撞到对象时 返回 true 没有 返回falseif (Physics.Raycast(r3, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal))
{print("碰撞到了对象");
}// 还有一种重载 不用传入 射线 直接传入起点 和 方向 也可以用于判断
// 就是把 第一个参数射线 变成了 射线的 两个点 一个起点 一个方向
if (Physics.Raycast(Vector3.zero, Vector3.forward, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal))
{print("碰撞到了对象2");
}
3.
法线与射线的关系
-
碰撞检测:
- 当射线投射(Raycasting)与物体发生碰撞时,法线提供了碰撞点处表面的朝向信息。
- 法线的方向可以帮助确定物体表面的朝向,这对于物理模拟(如反弹、摩擦等)和视觉效果(如反射、折射等)非常重要。
-
光照和着色:
- 在渲染过程中,法线用于计算物体在不同光照条件下的明暗变化。
- 法线与光源方向的夹角影响光照强度,从而影响物体的颜色和亮度。
法线的作用
- 表面朝向:法线垂直于物体表面,指向物体外部。这有助于确定物体表面的朝向。
- 光照计算:在光照模型中,法线用于计算光照效果,如漫反射、镜面反射等。
- 碰撞处理:在物理模拟中,法线用于计算碰撞后物体的反弹方向和摩擦力。
在射线投射中的法线
在射线投射中,如果射线与物体发生碰撞,RaycastHit
结构体中的 normal
属性会存储碰撞点处物体表面的法线。这可以用于:
- 确定碰撞点的朝向:通过法线的方向,可以知道物体在碰撞点的朝向。
- 物理模拟:在物理引擎中,法线用于计算碰撞后的反弹方向和摩擦力。
- 视觉效果:在渲染中,法线用于计算光照效果,如反射和折射。
在 Unity 中,您可以使用 RaycastHit
结构体中的 normal
属性来获取碰撞点处的法线。
-
Raycasting (射线投射):
Physics.Raycast
是 Unity 中用于检测射线是否与游戏世界中的物体发生碰撞的方法。- 射线有一个起点和一个方向,可以检测一定距离内的碰撞。
-
RaycastHit (射线击中信息):
RaycastHit
是一个结构体,用于存储射线投射的结果。- 当射线与物体发生碰撞时,
RaycastHit
会包含碰撞点、法线、碰撞物体等信息。
-
LayerMask (层级遮罩):
LayerMask
用于指定射线投射应该检测哪些层级的物体。- 通过
LayerMask.NameToLayer
方法可以将层级的名称转换为层级索引。
-
QueryTriggerInteraction (触发器交互):
- 控制射线投射是否应该与触发器发生交互。
UseGlobal
使用全局设置,Collide
表示检测触发器,Ignore
表示忽略触发器。
-
out 参数:
out
关键字用于定义一个输出参数,允许方法返回额外的信息。
-
重载方法:
Physics.Raycast
有多个重载版本,可以传入射线或起点和方向。
代码示例分析:
using UnityEngine;public class RaycastExample : MonoBehaviour
{void Update(){// 定义射线的起点和方向Ray ray = new Ray(transform.position, transform.forward);// 物体信息类 RaycastHitRaycastHit hitInfo;// 射线// RaycastHit 是结构体,是值类型。Unity 会通过 out 关键字在函数内部处理后,得到碰撞数据后返回到该参数中// 距离// 检测指定层级(不填检测所有层)// 是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用 UseGlobalif (Physics.Raycast(ray, out hitInfo, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal)){Debug.Log("碰撞到了物体 得到了信息");// 碰撞器信息Debug.Log("碰撞到物体的名字: " + hitInfo.collider.gameObject.name);// 碰撞到的点Debug.Log("碰撞到的点: " + hitInfo.point);// 法线信息Debug.Log("法线信息: " + hitInfo.normal);// 得到碰撞到对象的位置Debug.Log("碰撞到对象的位置: " + hitInfo.transform.position);// 得到碰撞到对象 离自己的距离Debug.Log("离自己的距离: " + hitInfo.distance);}else{Debug.Log("没有碰撞到任何物体");}// 另一种重载,不用传入射线,直接传入起点和方向if (Physics.Raycast(Vector3.zero, Vector3.forward, out hitInfo, 1000, 1 << LayerMask.NameToLayer("Monster"), QueryTriggerInteraction.UseGlobal)){Debug.Log("使用起点和方向的射线投射也碰撞到了物体");}else{Debug.Log("使用起点和方向的射线投射没有碰撞到任何物体");}}
}
4.
-
Physics.RaycastAll
方法用于从射线发出点开始,沿着射线方向检测所有碰撞到的物体。它返回一个RaycastHit
数组,每个元素包含碰撞信息。 -
参数说明:
- 参数一:射线(
Ray
或Vector3
起点和Vector3
方向的组合)。 - 参数二:最大检测距离。
- 参数三:层级掩码(
LayerMask
),用于指定检测哪些层级的物体。 - 参数四:触发器交互设置(
QueryTriggerInteraction
),用于决定是否检测触发器。
- 参数一:射线(
-
RaycastHit
结构体包含碰撞信息,如碰撞点、碰撞物体的GameObject
、碰撞法线等。 -
Physics.RaycastAll
有一个重载版本,允许直接使用起点和方向来创建射线。 -
Physics.RaycastNonAlloc
方法用于检测射线与物体的碰撞,但它不会分配新的数组,而是使用传入的数组来存储碰撞信息。如果发生碰撞,它会返回碰撞的数量。
根据这些知识点,下面是补全的代码:
// 定义射线的起点和方向
Ray ray = new Ray(Vector3.zero, Vector3.forward);// 定义最大检测距离
float maxDistance = 1000f;// 定义层级掩码,只检测名为"Monster"的层
LayerMask monsterLayer = 1 << LayerMask.NameToLayer("Monster");// 使用Physics.RaycastAll检测射线与物体的碰撞
RaycastHit[] hits = Physics.RaycastAll(ray, maxDistance, monsterLayer, QueryTriggerInteraction.UseGlobal);// 遍历所有碰撞结果并打印碰撞物体的名称
for (int i = 0; i < hits.Length; i++)
{print("碰到的所有物体 名字分别是 " + hits[i].collider.gameObject.name);
}// 使用重载版本的Physics.RaycastAll,直接传入起点和方向
hits = Physics.RaycastAll(Vector3.zero, Vector3.forward, maxDistance, monsterLayer, QueryTriggerInteraction.UseGlobal);// 使用Physics.RaycastNonAlloc检测射线与物体的碰撞,并使用已有的数组来存储结果
if (Physics.RaycastNonAlloc(ray, hits, maxDistance, monsterLayer, QueryTriggerInteraction.UseGlobal) > 0)
{// 如果有碰撞发生,遍历碰撞结果并打印碰撞物体的名称for (int i = 0; i < hits.Length; i++){if (hits[i].collider != null) // 确保hits数组中的元素不是空的{print("碰撞到的物体 名字是 " + hits[i].collider.gameObject.name);}}
}
5.
-
参数类型明确性:
- 在使用
Physics.Raycast
或相关方法时,需要明确每个参数的类型和它们代表的意义。 - 第二个参数应该是一个
float
类型,代表射线的最大检测距离。 - 第三个参数应该是一个
int
类型,代表层级掩码(LayerMask
),用于指定检测哪些层级的物体。
- 在使用
-
错误的参数传递:
- 在您的示例中,
Physics.Raycast(r3, 1000, 1 << LayerMask.NameToLayer("Monster"))
是错误的,因为第二个参数1000
应该是一个float
类型,而不是int
类型。
- 在您的示例中,
-
正确的参数传递:
- 正确的调用方式应该是将距离参数传递为
float
类型,例如Physics.Raycast(r3, maxDistance, 1 << LayerMask.NameToLayer("Monster"))
,其中maxDistance
是一个float
类型的变量。
- 正确的调用方式应该是将距离参数传递为
-
层级掩码的使用:
- 层级掩码(
LayerMask
)是一个int
类型的位掩码,用于指定哪些层级的物体应该被射线检测到。 - 使用
1 << LayerMask.NameToLayer("Monster")
可以创建一个只检测名为 "Monster" 层的层级掩码。
- 层级掩码(
-
参数顺序:
- 确保参数的顺序正确,即射线、距离、层级掩码、触发器交互设置。
-
触发器交互设置:
QueryTriggerInteraction
枚举用于控制是否检测触发器。它可以是UseGlobal
、Collide
或Ignore
。
总结这些知识点,您可以修正代码如下:
// 定义射线
Ray r3 = new Ray(Vector3.zero, Vector3.forward);// 定义最大检测距离,注意这里是float类型
float maxDistance = 1000.0f;// 定义层级掩码,只检测名为"Monster"的层
LayerMask monsterLayer = 1 << LayerMask.NameToLayer("Monster");// 正确的调用方式,注意距离参数是float类型
if (Physics.Raycast(r3, maxDistance, monsterLayer))
{// 碰撞检测到的逻辑
}