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可能未立即刷新碰撞体数据,导致短暂的位置偏差。
修复方案
- 统一顶点坐标空间:将自定义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));
- 修正三角面顺序:确保每个面的三角面组合从外部观察为逆时针,例如调整
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); }
- 适配Transform缩放的射线检测:使用
lossyScale计算实际射线长度:
float rightRayLength = BlockScale * currentBlock.lossyScale.x; if (!Physics.Raycast(currentBlock.position, Vector3.right, rightRayLength, groundLayer)) GenerateRightSide();
- 强制刷新MeshCollider:赋值
sharedMesh后,通过开关组件状态强制刷新:
var collider = currentBlock.GetComponent<MeshCollider>(); collider.sharedMesh = cubeMesh; collider.enabled = false; collider.enabled = true;
内容的提问来源于stack exchange,提问作者Nippppd
相关产品推荐
相关产品推荐

