射线检测
🎯 Unity 射线检测(Raycast)
目录
- 什么是射线检测
- 射线对象 Ray
- Raycast 核心 API
- RaycastHit 返回的数据
- LayerMask 与过滤规则
- 实战技巧(根据你写的 FireWall 代码扩展)
- 完整示例代码
- 常见错误与调试方法
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);

浙公网安备 33010602011771号