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

Unity烘焙后Mesh与Collider位置不一致的原因排查

Unity渲染Mesh与碰撞体位置偏差问题排查

我正在开发一款用于合并网格、生成优化碰撞体的世界烘焙系统,运行烘焙脚本后发现,烘焙对象的可视化Mesh与Collider位置存在不一致的问题——明明二者使用相同的网格数据,却出现了位置偏差。以下是问题代码及原因分析:

问题代码

using UnityEngine;
using System.Collections.Generic;
using System;

public class WorldBaker2 : MonoBehaviour
{
  [SerializeField] private List<GameObject> blocksToBake = new List<GameObject>();
  [SerializeField] private LayerMask groundLayer;

  const float BlockScale = 1;

  private Mesh cubeMesh;
  private Transform currentBlock;

  private List<Vector3> verticies = new List<Vector3>();
  private List<Vector2> uvs = new List<Vector2>();
  private List<int> triangles = new List<int>();

  [ContextMenu("Bake World")]
  public void BakeWorld()
  {
    foreach (var item in blocksToBake)
    {
      currentBlock = item.transform;
      cubeMesh = new Mesh();

      // 删除现有碰撞体
      Collider existingCollider = currentBlock.gameObject.GetComponent<Collider>();
      if (existingCollider != null)
      {
        UnityEngine.Object.Destroy(existingCollider);
      }
      currentBlock.gameObject.AddComponent<MeshCollider>();

      RemakeMesh();
      item.GetComponent<MeshFilter>().sharedMesh = cubeMesh;
    }

    print("baked");
  }

  private void RemakeMesh()
  {
    verticies.Clear();
    uvs.Clear();
    triangles.Clear();

    RemakeBlock();

    cubeMesh.triangles = Array.Empty<int>();
    cubeMesh.vertices = verticies.ToArray();
    cubeMesh.uv = uvs.ToArray();
    cubeMesh.triangles = triangles.ToArray();

    cubeMesh.Optimize();
    cubeMesh.RecalculateNormals();
    cubeMesh.RecalculateBounds();

    currentBlock.GetComponent<MeshCollider>().sharedMesh = cubeMesh;
  }

  private void RemakeBlock()
  {
    if (!Physics.Raycast(currentBlock.position, Vector3.right, BlockScale, groundLayer)) GenerateRightSide();
    if (!Physics.Raycast(currentBlock.position, Vector3.left, BlockScale, groundLayer)) GenerateLeftSide();
    if (!Physics.Raycast(currentBlock.position, Vector3.forward, BlockScale, groundLayer)) GenerateFrontSide();
    if (!Physics.Raycast(currentBlock.position, Vector3.back, BlockScale, groundLayer)) GenerateBackSide();
    if (!Physics.Raycast(currentBlock.position, Vector3.up, BlockScale, groundLayer)) GenerateTopSide();
    if (!Physics.Raycast(currentBlock.position, Vector3.down, BlockScale, groundLayer)) GenerateBottomSide();
  }

  private void GenerateRightSide()
  {
    verticies.Add(new Vector3(1, 0, 0));
    verticies.Add(new Vector3(1, 1, 0));
    verticies.Add(new Vector3(1, 0, 1));
    verticies.Add(new Vector3(1, 1, 1));
    AddLastVerticiesSquare();
  }

  private void GenerateLeftSide()
  {
    print("generate left side");
    verticies.Add(new Vector3(0, 0, 0));
    verticies.Add(new Vector3(0, 0, 1));
    verticies.Add(new Vector3(0, 1, 0));
    verticies.Add(new Vector3(0, 1, 1));
    AddLastVerticiesSquare();
  }

  private void GenerateFrontSide()
  {
    verticies.Add(new Vector3(0, 0, 1));
    verticies.Add(new Vector3(1, 0, 1));
    verticies.Add(new Vector3(0, 1, 1));
    verticies.Add(new Vector3(1, 1, 1));
    AddLastVerticiesSquare();
  }

  private void GenerateBackSide()
  {
    verticies.Add(new Vector3(0, 0, 0));
    verticies.Add(new Vector3(0, 1, 0));
    verticies.Add(new Vector3(1, 0, 0));
    verticies.Add(new Vector3(1, 1, 0));
    AddLastVerticiesSquare();
  }

  private void GenerateTopSide()
  {
    verticies.Add(new Vector3(0, 1, 0));
    verticies.Add(new Vector3(0, 1, 1));
    verticies.Add(new Vector3(1, 1, 0));
    verticies.Add(new Vector3(1, 1, 1));
    AddLastVerticiesSquare();
  }

  private void GenerateBottomSide()
  {
    verticies.Add(new Vector3(0, 0, 0));
    verticies.Add(new Vector3(1, 0, 0));
    verticies.Add(new Vector3(0, 0, 1));
    verticies.Add(new Vector3(1, 0, 1));
    AddLastVerticiesSquare();
  }

  private void AddLastVerticiesSquare()
  {
    uvs.Add(new Vector2(0, 0));
    uvs.Add(new Vector2(0, 1));
    uvs.Add(new Vector2(1, 0));
    uvs.Add(new Vector2(1, 1));

    triangles.Add(verticies.Count - 4);
    triangles.Add(verticies.Count - 3);
    triangles.Add(verticies.Count - 2);

    triangles.Add(verticies.Count - 3);
    triangles.Add(verticies.Count - 1);
    triangles.Add(verticies.Count - 2);
  }
}

导致Mesh与Collider位置偏差的核心原因

1. 顶点坐标空间不匹配

Unity默认Cube的Mesh顶点范围是-0.5到0.5,中心对齐局部空间原点(0,0,0);而你生成的Mesh顶点是0到1,原点位于立方体角落。当原Block的Transform位置为世界坐标时,渲染Mesh与碰撞体的世界位置会整体偏移0.5个单位,和预期位置产生偏差。

2. 三角面顺序错误(背面剔除导致视觉偏差)

Unity默认以逆时针顺序的三角面为正面,若顺序错误,渲染时会触发背面剔除,导致部分面不可见,但碰撞体仍会识别完整Mesh,看起来就像Mesh和Collider位置不符。例如GenerateLeftSide的顶点顺序和三角面组合,从左侧观察是顺时针,会被判定为背面而不渲染。

3. 射线检测未考虑Transform缩放

代码中射线长度固定为BlockScale=1,若原Block的Transform存在缩放,射线实际检测范围会和Block的世界尺寸不匹配,导致错误生成多余的Mesh面,进一步加剧视觉与碰撞体的差异。

4. MeshCollider刷新不及时

在赋值sharedMesh后,MeshCollider可能未立即刷新碰撞体数据,导致短暂的位置偏差。

修复方案

  1. 统一顶点坐标空间:将自定义Mesh的顶点改为-0.5到0.5范围,对齐Unity默认Cube的坐标体系,例如修改GenerateRightSide:
verticies.Add(new Vector3(0.5f, -0.5f, -0.5f));
verticies.Add(new Vector3(0.5f, 0.5f, -0.5f));
verticies.Add(new Vector3(0.5f, -0.5f, 0.5f));
verticies.Add(new Vector3(0.5f, 0.5f, 0.5f));
  1. 修正三角面顺序:确保每个面的三角面组合从外部观察为逆时针,例如调整GenerateLeftSide的顶点与三角面:
private void GenerateLeftSide()
{
    verticies.Add(new Vector3(-0.5f, -0.5f, -0.5f));
    verticies.Add(new Vector3(-0.5f, -0.5f, 0.5f));
    verticies.Add(new Vector3(-0.5f, 0.5f, 0.5f));
    verticies.Add(new Vector3(-0.5f, 0.5f, -0.5f));

    triangles.Add(verticies.Count - 4);
    triangles.Add(verticies.Count - 2);
    triangles.Add(verticies.Count - 3);
    triangles.Add(verticies.Count - 4);
    triangles.Add(verticies.Count - 1);
    triangles.Add(verticies.Count - 2);
}
  1. 适配Transform缩放的射线检测:使用lossyScale计算实际射线长度:
float rightRayLength = BlockScale * currentBlock.lossyScale.x;
if (!Physics.Raycast(currentBlock.position, Vector3.right, rightRayLength, groundLayer)) GenerateRightSide();
  1. 强制刷新MeshCollider:赋值sharedMesh后,通过开关组件状态强制刷新:
var collider = currentBlock.GetComponent<MeshCollider>();
collider.sharedMesh = cubeMesh;
collider.enabled = false;
collider.enabled = true;

内容的提问来源于stack exchange,提问作者Nippppd

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 00:44:52