Unity中Raycast射线无法检测MeshCollider网格碰撞体问题
问题背景
实现凸包生成逻辑时,为让每个三角面显示不同颜色,为凸包的每个面/三角面单独实例化挂载MeshRenderer、MeshFilter、MeshCollider组件的GameObject。后续计划通过键盘按键控制射线切换方向:射线以凸包质心为起点,沿预设点列表中各点位对应的方向发射,检测路径上的目标碰撞体。
故障现象
射线始终无法检测到MeshCollider,仅能正常检测SphereCollider球体碰撞体。已尝试以下排查方案,均未解决问题:
- 将射线逻辑从
Update生命周期移至FixedUpdate执行 - 将凸包生成与射线检测逻辑合并到同一脚本
- 将所有三角面网格合并到同一个对象下,排查非凸
MeshCollider的检测限制问题 - 新建射线未设置忽略规则的专属图层存放网格对象
相关实现代码
凸包生成代码
void CreateMyMesh() { for (int i = 0; i < faces.Count; i++) { if (faces[i].points.Count == 3) { Vector3 directionOfTri = Vector3.Cross( faces[i].points[1] - faces[i].points[0], faces[i].points[2] - faces[i].points[0] ).normalized; Vector3 centroidTri = new Vector3( (faces[i].points[0].x + faces[i].points[1].x + faces[i].points[2].x) / 3, (faces[i].points[0].y + faces[i].points[1].y + faces[i].points[2].y) / 3, (faces[i].points[0].z + faces[i].points[1].z + faces[i].points[2].z) / 3 ); float sign = Vector3.Dot(directionOfTri, centroid - centroidTri); if (sign > 0) { for (int j = 2; j >= 0; j--) { Vector3 v = new Vector3( faces[i].points[j].x, faces[i].points[j].y, faces[i].points[j].z ); int iWhich = isExist(v, tmpVec); if (iWhich == -1) { tmpVec.Add(v); tmpTriangles.Add(tmpVec.Count - 1); } else { tmpTriangles.Add(iWhich); } } } else { for (int j = 0; j < 3; j++) { Vector3 v = new Vector3( faces[i].points[j].x, faces[i].points[j].y, faces[i].points[j].z ); int iWhich = isExist(v, tmpVec); if (iWhich == -1) { tmpVec.Add(v); tmpTriangles.Add(tmpVec.Count - 1); } else { tmpTriangles.Add(iWhich); } } } } else { print("Error : #of points in Face != 3"); } GameObject newMeshObject = Instantiate(MeshObject, MeshObject.transform.position, Quaternion.identity); newMeshObject.name = "triangle number " + i; Mesh mesh = new Mesh(); newMeshObject.GetComponent<MeshRenderer>().material.color = new Color(tmpVec[0].x, tmpVec[0].y, tmpVec[0].z, 1f); newMeshObject.GetComponent<MeshFilter>().mesh = mesh; newMeshObject.GetComponent<MeshCollider>().sharedMesh = mesh; mesh.Clear(); mesh.vertices = tmpVec.ToArray(); mesh.triangles = tmpTriangles.ToArray(); mesh.RecalculateNormals(); mesh.name = "triangle mesh number " + i; tmpVec.Clear(); tmpTriangles.Clear(); } }
射线检测代码
int ct = 0; public void Raycasttt() { if (Input.GetKeyDown(KeyCode.UpArrow)) { if (ct < rayyy.Count - 1) { ct++; } else { ct = 0; } } Debug.DrawRay(transform.position, rayyy[ct] * 10, Color.yellow); RaycastHit hit; Vector3 Direc = (rayyy[ct] * 10 - transform.position).normalized; if (Physics.Raycast(transform.position, Direc, out hit, Mathf.Infinity, 6)) { Debug.Log("in raycast"); if (hit.collider != null) { Debug.DrawRay(transform.position, rayyy[ct] * 10 * hit.distance, Color.yellow); Debug.Log("Did Hit"); Debug.Log("HitPosition = " + hit.collider.gameObject.transform.position); } Debug.Log(hit.collider.gameObject.name); } }
预制体配置
实例化所用的MeshObject预制体配置如下:
问题原因与修复方案
1. 射线层掩码参数使用错误
Physics.Raycast的layerMask参数需要传入位掩码,而非直接传入层序号。代码中直接传入6,对应二进制为00000110,实际只会检测第1、2层的碰撞体,其中第2层还是默认的Ignore Raycast层,如果网格对象存放在第6层,完全不在检测范围内。
- 修复方式:将层参数改为位偏移写法,检测第6层需传入
1 << 6;如果需要检测多个层,用按位或拼接即可,比如(1 << 6) | (1 << 0)表示同时检测默认层和第6层。
修改后的射线检测核心代码:
int meshLayerMask = 1 << 6; if (Physics.Raycast(transform.position, Direc, out hit, Mathf.Infinity, meshLayerMask)) { // 原有命中逻辑 }
2. MeshCollider赋值时机错误,碰撞网格未生效
Unity的MeshCollider不会自动响应绑定后Mesh实例的数据修改,代码中先把空Mesh赋值给MeshCollider.sharedMesh,后续才填充顶点、三角形数据,会导致MeshCollider读取到的网格是空的无效数据,无法生成有效碰撞体。
- 修复方式:调整代码顺序,等Mesh的所有数据(顶点、三角形、法线)全部赋值完成后,再将Mesh赋值给MeshCollider.sharedMesh。
调整后的凸包生成核心代码段:
GameObject newMeshObject = Instantiate(MeshObject, MeshObject.transform.position, Quaternion.identity); newMeshObject.name = "triangle number " + i; Mesh mesh = new Mesh(); // 先填充所有网格数据 mesh.vertices = tmpVec.ToArray(); mesh.triangles = tmpTriangles.ToArray(); mesh.RecalculateNormals(); mesh.name = "triangle mesh number " + i; // 数据填充完成后再赋值给渲染和碰撞组件 newMeshObject.GetComponent<MeshRenderer>().material.color = new Color(tmpVec[0].x, tmpVec[0].y, tmpVec[0].z, 1f); newMeshObject.GetComponent<MeshFilter>().mesh = mesh; newMeshObject.GetComponent<MeshCollider>().sharedMesh = mesh; tmpVec.Clear(); tmpTriangles.Clear();
补充:如果后续需要动态修改网格,修改完成后可以先将sharedMesh设为null再重新赋值,强制MeshCollider刷新碰撞数据。
3. 射线命中三角面背面,单面碰撞不响应
生成的凸包三角面法线统一朝向凸包外侧,而射线起点是凸包质心(凸包内部),射线从内向外发射时会全部打在三角面的背面。Unity默认MeshCollider为单面碰撞,仅法线朝向的正面会产生碰撞响应,背面无法被射线命中。
- 修复方式按需选择:
- 如果射线仅需要从凸包内部向外检测:调整三角面顶点顺序,将三角面法线翻转朝向凸包内部,让射线命中正面。
- 如果需要双向检测:给每个三角面的网格增加一组反向排列的三角形,生成双面网格,让正反两面都有碰撞,避免修改全局Physics设置带来的其他问题。
- 如果业务逻辑允许:将射线起点移动到凸包外部,朝向凸包质心发射,即可命中朝外的三角面正面。
内容的提问来源于stack exchange,提问作者Ash10

