射线检测

🎯 Unity 射线检测(Raycast)

目录

  1. 什么是射线检测
  2. 射线对象 Ray
  3. Raycast 核心 API
  4. RaycastHit 返回的数据
  5. LayerMask 与过滤规则
  6. 实战技巧(根据你写的 FireWall 代码扩展)
  7. 完整示例代码
  8. 常见错误与调试方法

1. 什么是射线检测(Raycast)

射线检测是 Unity 物理系统提供的一种 瞬时碰撞检测方式

  • 不需要刚体
  • 只判断“一条射线是否撞到任何碰撞器”
    常用于:
    ✔ 鼠标点击选中物体
    ✔ 射击类 FPS(无实体子弹)
    ✔ 角色点击移动
    ✔ UI/3D 交互
    ✔ 视线检测(AI 视野)

它判断的是 一条无限细的直线碰撞器的相交情况。


2. 射线对象 Ray

Unity 的 Ray 由两部分组成:

  • origin:起点
  • direction:方向(是向量,不是两个点!)
    示例:
Ray r = new Ray(Vector3.right, Vector3.forward);
print(r.origin);     // (1,0,0)
print(r.direction);  // (0,0,1)

2.1 从摄像机构建射线(最常用)

鼠标点击 3D 世界几乎都靠它:需要注意摄像机的使用,如果使用主摄像机就使用Camera.main,如果使用的是UI摄像机就要先获取UICamera再使用UICamera

//使用主摄像机
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
//使用UICamera
private Camera UICamera;
UICamera = GameObject.Find("UICamera").GetComponent<Camera>();
Ray ray = UICamera.ScreenPointToRay(Input.mousePosition);

它表示:
从摄像机位置出发,朝鼠标看向的方向发射射线。
可以使用 Debug.DrawRay(ray.origin,ray.direction); 画出射线


3. Raycast 核心 API

Unity 为 Raycast 提供大量重载。记住下面三类即可。

3.1 只判断是否撞到对象(返回 bool)

if(Physics.Raycast(ray, 1000, layerMask)){};

参数说明:

参数 含义
ray 射线
1000 最大检测距离
layerMask 指定层(可过滤无关物体)

3.2 获取单个碰撞信息(RaycastHit)

RaycastHit hitInfo;
//重载一 QueryTriggerInteraction.UseGlobal可以默认不写
if (Physics.Raycast(ray, out hitInfo, 1000, layerMask, QueryTriggerInteraction.UseGlobal))
{
    Debug.Log(hitInfo.collider.name);
}
//重载二
if (Physics.Raycast(origin, direction, out hitInfo, 1000, layerMask, QueryTriggerInteraction.UseGlobal))
{
    Debug.Log(hitInfo.collider.name);
}

返回第一个被击中的物体(按距离最近排序)。

3.3 获取多个碰撞信息(RaycastAll)

//方法一 RaycastAll
//重载一
RaycastHit[] hits = Physics.RaycastAll(ray, 1000, layerMask);
//重载二
RaycastHit[] hits = Physics.RaycastAll(origin, direction, 1000, layerMask);

//方法二 RaycastNonAlloc
//results自定义RaycastHit数组
RaycastHit[] results = new RaycastHit[10];
if(Physics.RaycastNonAlloc(r3, results, 1000, 1 << LayerMask.NameToLayer("Enemy"), QueryTriggerInteraction.UseGlobal) > 0)
{
  
}

返回所有击中的物体(由远及近存入RaycastHit中)。


4. RaycastHit 返回的数据

RaycastHit 是射线最重要的结构体,包含这几个关键字段:

字段 含义
collider 被射线击中的 Collider
transform 物体的 Transform
point 击中位置(世界坐标)
normal 击中表面的法线方向
distance 起点到击中点的距离
最常用组合:
hitInfo.point                      // 命中点位置
hitInfo.normal                     // 命中面的方向
Quaternion.LookRotation(hitInfo.normal) // 可以让子弹命中特效朝向法线方向

5. LayerMask 与过滤规则

射线本身会“检测可交互物体”,会被Text Image等等给挡住
我们使用LayerMask就可以只检测“我们想要射线检测的物体”,可以避免被其它层级遮挡,搭配Collider碰撞器,可以只检测想要检测的层级里带碰撞器的物体,避免被该层级的Text Image等等挡住

int mask = 1 << LayerMask.NameToLayer("Enemy");
Physics.Raycast(ray, out hitInfo, 1000, mask);

6. 实战技巧(你的 FireWall 案例中的重点)

基于你写的 HitEffect 示例,总结出这些常用技巧:

6.1 使用 RaycastHit.point 获得命中点

Vector3 pos = hitInfo.point;

用于:

  • 生成子弹击中效果
  • 在目标表面贴 decal
  • 做点击移动

6.2 使用 RaycastHit.normal 获取表面朝向

obj.transform.rotation = Quaternion.LookRotation(hitInfo.normal);

用途:

  • 特效朝向正确
  • 子弹孔始终“贴住”表面
  • 避免穿模

6.3 命中点上偏移一点避免穿进去

pos + hitInfo.normal * 0.2f

6.4 使用 GetMouseButtonDown 避免连续触发射线

if (Input.GetMouseButtonDown(0))

适合射击类操作。

6.5 实例化一次性特效并销毁

GameObject fx = Instantiate(hitFx);
Destroy(fx, 0.8f);

7. 完整示例(包含击中特效与子弹孔)

using UnityEngine;
public class FireWall : MonoBehaviour
{
    private RaycastHit info;

    void Update()
    {
        // 鼠标左键射击
        if (Input.GetMouseButtonDown(0))
            DoRaycast();
    }

    private void DoRaycast()
    {
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

        int layer = 1 << LayerMask.NameToLayer("Enemy");

        if (Physics.Raycast(ray, out info, 1000, layer))
        {
            Vector3 pos = info.point;

            // 击中特效
            GameObject fx = Instantiate(Resources.Load<GameObject>("Effect/HitEff"));
            fx.transform.position = pos + info.normal * 0.2f;
            fx.transform.rotation = Quaternion.LookRotation(info.normal);
            Destroy(fx, 0.8f);

            // 子弹孔
            GameObject hole = Instantiate(Resources.Load<GameObject>("Effect/BallHole"));
            hole.transform.position = pos + info.normal * 0.2f;
            hole.transform.rotation = Quaternion.LookRotation(info.normal);
        }
    }
}

8. 常见错误与调试技巧

❌ 没加 Collider

→ 射线永远打不到物体

❌ LayerMask 设置错

→ Raycast 忽略目标物体

❌ Debug.DrawRay 用错参数

第二个参数是 方向不是终点
✔ 正确

Debug.DrawRay(ray.origin, ray.direction * 5, Color.red);

✔ 使用 Debug.DrawLine 可视化射线

Debug.DrawLine(ray.origin, ray.origin + ray.direction * 1000, Color.green);

posted @ 2025-12-04 19:18  高山仰止666  阅读(7)  评论(0)    收藏  举报