You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中Raycast射线无法检测MeshCollider网格碰撞体问题

Unity射线无法检测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预制体配置如下:
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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 02:39:50