自学内容网 自学内容网

unity3d————射线检测

2

  1. 射线(Ray)

    • Ray结构体包含两个主要部分:一个起点(origin)和一个方向(direction
  2. 射线检测(Raycast)

    • Physics.Raycast方法用于执行射线检测。
    • 参数包括:
      • 射线(Ray或起点和方向向量)。
      • 检测的最大距离。
      • 检测指定层级(使用LayerMask)。
      • 是否忽略触发器(QueryTriggerInteraction)。
  3. LayerMask

    • 用于指定射线检测应该考虑哪些层级的物体。
    • LayerMask.NameToLayer方法将层级的名称转换为对应的层级索引。
  4. QueryTriggerInteraction

    • 控制射线检测是否应该与触发器(Collider带有Is Trigger属性的)发生交互。
    • 选项包括:
      • UseGlobal:使用全局设置(Physics.queriesHitTriggers)。
      • Collide:总是与触发器交互。
      • Ignore:忽略触发器。
  5. 重载方法

    • Physics.Raycast有多个重载版本,允许不同的参数传递方式。

代码示例分析:

// 1. 最原始的射线检测
// 准备一条射线
Ray r3 = new Ray(Vector3.zero, Vector3.forward);
// 进行射线检测 如果碰撞到对象 返回true
// 参数一:射线
// 参数二: 检测的最大距离 超出这个距离不检测
// 参数三:检测指定层级(不填检测所有层)
// 参数四:是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用UseGlobal
// 返回值:bool 当碰撞到对象时 返回 true 没有 返回false

if (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.

法线与射线的关系

  1. 碰撞检测

    • 当射线投射(Raycasting)与物体发生碰撞时,法线提供了碰撞点处表面的朝向信息。
    • 法线的方向可以帮助确定物体表面的朝向,这对于物理模拟(如反弹、摩擦等)和视觉效果(如反射、折射等)非常重要。
  2. 光照和着色

    • 在渲染过程中,法线用于计算物体在不同光照条件下的明暗变化。
    • 法线与光源方向的夹角影响光照强度,从而影响物体的颜色和亮度。

法线的作用

  • 表面朝向:法线垂直于物体表面,指向物体外部。这有助于确定物体表面的朝向。
  • 光照计算:在光照模型中,法线用于计算光照效果,如漫反射、镜面反射等。
  • 碰撞处理:在物理模拟中,法线用于计算碰撞后物体的反弹方向和摩擦力。

在射线投射中的法线

在射线投射中,如果射线与物体发生碰撞,RaycastHit 结构体中的 normal 属性会存储碰撞点处物体表面的法线。这可以用于:

  • 确定碰撞点的朝向:通过法线的方向,可以知道物体在碰撞点的朝向。
  • 物理模拟:在物理引擎中,法线用于计算碰撞后的反弹方向和摩擦力。
  • 视觉效果:在渲染中,法线用于计算光照效果,如反射和折射。 

在 Unity 中,您可以使用 RaycastHit 结构体中的 normal 属性来获取碰撞点处的法线。

  1. Raycasting (射线投射):

    • Physics.Raycast 是 Unity 中用于检测射线是否与游戏世界中的物体发生碰撞的方法。
    • 射线有一个起点和一个方向,可以检测一定距离内的碰撞。
  2. RaycastHit (射线击中信息):

    • RaycastHit 是一个结构体,用于存储射线投射的结果。
    • 当射线与物体发生碰撞时,RaycastHit 会包含碰撞点、法线、碰撞物体等信息。
  3. LayerMask (层级遮罩):

    • LayerMask 用于指定射线投射应该检测哪些层级的物体。
    • 通过 LayerMask.NameToLayer 方法可以将层级的名称转换为层级索引。
  4. QueryTriggerInteraction (触发器交互):

    • 控制射线投射是否应该与触发器发生交互。
    • UseGlobal 使用全局设置,Collide 表示检测触发器,Ignore 表示忽略触发器。
  5. out 参数:

    • out 关键字用于定义一个输出参数,允许方法返回额外的信息。
  6. 重载方法:

    • Physics.Raycast 有多个重载版本,可以传入射线或起点和方向。

   代码示例分析: 

using UnityEngine;

public class RaycastExample : MonoBehaviour
{
    void Update()
    {
        // 定义射线的起点和方向
        Ray ray = new Ray(transform.position, transform.forward);
        
        // 物体信息类 RaycastHit
        RaycastHit hitInfo;
        
        // 射线
        // RaycastHit 是结构体,是值类型。Unity 会通过 out 关键字在函数内部处理后,得到碰撞数据后返回到该参数中
        // 距离
        // 检测指定层级(不填检测所有层)
        // 是否忽略触发器 UseGlobal-使用全局设置 Collide-检测触发器 Ignore-忽略触发器 不填使用 UseGlobal
        if (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.

  1. Physics.RaycastAll 方法用于从射线发出点开始,沿着射线方向检测所有碰撞到的物体。它返回一个 RaycastHit 数组,每个元素包含碰撞信息。

  2. 参数说明:

    • 参数一:射线(Ray 或 Vector3 起点和 Vector3 方向的组合)。
    • 参数二:最大检测距离。
    • 参数三:层级掩码(LayerMask),用于指定检测哪些层级的物体。
    • 参数四:触发器交互设置(QueryTriggerInteraction),用于决定是否检测触发器。
  3. RaycastHit 结构体包含碰撞信息,如碰撞点、碰撞物体的 GameObject、碰撞法线等。

  4. Physics.RaycastAll 有一个重载版本,允许直接使用起点和方向来创建射线。

  5. 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.

  1. 参数类型明确性

    • 在使用 Physics.Raycast 或相关方法时,需要明确每个参数的类型和它们代表的意义。
    • 第二个参数应该是一个 float 类型,代表射线的最大检测距离。
    • 第三个参数应该是一个 int 类型,代表层级掩码(LayerMask),用于指定检测哪些层级的物体。
  2. 错误的参数传递

    • 在您的示例中,Physics.Raycast(r3, 1000, 1 << LayerMask.NameToLayer("Monster")) 是错误的,因为第二个参数 1000 应该是一个 float 类型,而不是 int 类型。
  3. 正确的参数传递

    • 正确的调用方式应该是将距离参数传递为 float 类型,例如 Physics.Raycast(r3, maxDistance, 1 << LayerMask.NameToLayer("Monster")),其中 maxDistance 是一个 float 类型的变量。
  4. 层级掩码的使用

    • 层级掩码(LayerMask)是一个 int 类型的位掩码,用于指定哪些层级的物体应该被射线检测到。
    • 使用 1 << LayerMask.NameToLayer("Monster") 可以创建一个只检测名为 "Monster" 层的层级掩码。
  5. 参数顺序

    • 确保参数的顺序正确,即射线、距离、层级掩码、触发器交互设置。
  6. 触发器交互设置

    • QueryTriggerInteraction 枚举用于控制是否检测触发器。它可以是 UseGlobalCollide 或 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))
{
    // 碰撞检测到的逻辑
}


原文地址:https://blog.csdn.net/2401_82978699/article/details/143865894

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!