Unity中基于立方体生成体素网格球体的异常问题排查
立方体拼接球体的Mesh生成问题
我想生成由立方体拼接成的球体(效果如下):
直接用独立GameObject创建每个立方体的话,大尺寸球体(尤其是实心款)会有严重性能问题,查资料后确定正确思路是只生成暴露在外的面,合并成单个Mesh。我参考教程写了自定义脚本,但不管设置多大的planetDia(比如30),最终都只生成2x2x2的立方体,完全不是预期的空心球体Mesh。
我怀疑问题可能出在:
- 脚本挂载的GameObject类型错误
- 缺失必要组件
- 跳过了UV部分
附上我的代码:
public class PlanetGeneration : MonoBehaviour { private int planetDia = 30; private int planetRadius; //array of bools to keep track if each space is solid(true) or void(false) private bool[,,] currentPlanet; void Start() { currentPlanet = new bool[planetDia, planetDia, planetDia]; planetRadius = planetDia / 2 + 1; GeneratePlanet(); GenerateMesh(); } //define each block as void or solid public void GeneratePlanet() { Vector3 planetCenter = new(planetRadius, planetRadius, planetRadius); Vector3 currentPoint; for (int x = 0; x < planetDia; x++) { for (int y = 0; y < planetDia; y++) { for (int z = 0; z < planetDia; z++) { //set the current position currentPoint = new Vector3(x, y, z); //check if this point is within the desired planet size if (Vector3.Distance(planetCenter, currentPoint) <= planetRadius) { //make it a solid block currentPlanet[x, y, z] = true; } //make it void else currentPlanet[x, y, z] = false; } } } } //generate mesh public void GenerateMesh() { List<Vector3> Vertices = new List<Vector3>(); List<int> Triangles = new List<int>(); for (int x = 0; x < planetDia; x++) { for (int y = 0; y < planetDia; y++) { for (int z = 0; z < planetDia; z++) { //the 8 corners of a cube Vector3[] VertexPos = new Vector3[8] { new Vector3(-1, 1, -1), new Vector3(-1, 1, 1), new Vector3(1, 1, 1), new Vector3(1, 1, -1), new Vector3(-1, -1, -1), new Vector3(-1, -1, 1), new Vector3(1, -1, 1), new Vector3(1, -1, -1), }; //first 4 digits are the vertices of the face, and the last 3 are the relative coords of the next block in that direction int[,] Faces = new int[6, 7]{ {0, 1, 2, 3, 0, 1, 0}, //top {7, 6, 5, 4, 0, -1, 0}, //bottom {2, 1, 5, 6, 0, 0, 1}, //right {0, 3, 7, 4, 0, 0, -1}, //left {3, 2, 6, 7, 1, 0, 0}, //front {1, 0, 4, 5, -1, 0, 0} //back }; //if this block is solid if (currentPlanet[x, y, z] == true) { //for each face of the block for (int i = 0; i < 6; i++) { //check if it is on the edge of the overall array //This part is dumb and needs to be removed. Keeping for now to prevent the array out of bounds error. if(x + Faces[i, 4] == -1 || y + Faces[i, 5] == -1 || z + Faces[i, 6] == -1 || x + Faces[i, 4] == planetDia || y + Faces[i, 5] == planetDia || z + Faces[i, 6] == planetDia) { //do nothing } //check if the block next to it is void else if(currentPlanet[x + Faces[i, 4], y + Faces[i, 5], z + Faces[i, 6]] == false) { AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count); } } } void AddQuad(int ai, int bi, int ci, int di, int i) { Vector3 a = VertexPos[ai]; Vector3 b = VertexPos[bi]; Vector3 c = VertexPos[ci]; Vector3 d = VertexPos[di]; Vertices.AddRange(new List<Vector3>() { a, b, c, d }); Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 }); } } } } this.gameObject.GetComponent<MeshFilter>().mesh = new Mesh() { vertices = Vertices.ToArray(), triangles = Triangles.ToArray(), }; } }
问题排查与修复方案
1. 顶点未做空间偏移,所有面叠在原点
你的VertexPos只定义了单个立方体的局部顶点,没有加上当前方块的坐标偏移(x,y,z),导致所有生成的面都挤在原点附近,看起来就是个2x2x2的块。
修改AddQuad方法里的顶点计算:
void AddQuad(int ai, int bi, int ci, int di, int i) { // 加上当前方块的坐标偏移 Vector3 a = VertexPos[ai] + new Vector3(x, y, z); Vector3 b = VertexPos[bi] + new Vector3(x, y, z); Vector3 c = VertexPos[ci] + new Vector3(x, y, z); Vector3 d = VertexPos[di] + new Vector3(x, y, z); Vertices.AddRange(new List<Vector3>() { a, b, c, d }); Triangles.AddRange(new List<int>() { i, i + 1, i + 2, i, i + 2, i + 3 }); }
2. 球体半径计算错误,实心区域过大
你写的planetRadius = planetDia / 2 + 1会让球体判定范围远超预期,比如直径30时半径算成16,整个30x30x30的空间几乎全是实心,只有最外层几个方块是空的,自然看不到球体形状。
修改半径计算:
planetRadius = planetDia / 2;
另外,Vector3.Distance计算距离效率很低,建议改用平方距离比较,优化性能:
// 替换GeneratePlanet里的距离判断逻辑 if (Vector3.SqrMagnitude(currentPoint - planetCenter) <= planetRadius * planetRadius)
3. 边界判断逻辑错误,边缘面未生成
你当前的代码直接跳过了边缘方块的面,但实际上边缘方块外侧没有相邻方块,这些面应该被生成。修改边界判断逻辑:
// 替换GenerateMesh里的面生成判断逻辑 for (int i = 0; i < 6; i++) { int nx = x + Faces[i, 4]; int ny = y + Faces[i, 5]; int nz = z + Faces[i, 6]; // 相邻方块不存在(超出边界)或者是空心,就生成当前面 bool needGenerateFace = false; if (nx < 0 || nx >= planetDia || ny < 0 || ny >= planetDia || nz < 0 || nz >= planetDia) { needGenerateFace = true; } else { needGenerateFace = !currentPlanet[nx, ny, nz]; } if (needGenerateFace) { AddQuad(Faces[i, 0], Faces[i, 1], Faces[i, 2], Faces[i, 3], Vertices.Count); } }
4. 组件检查
确保脚本挂载的GameObject上有MeshFilter和MeshRenderer组件,后者必须添加材质才能看到生成的Mesh。UV缺失不会导致Mesh不显示,只是材质会拉伸,不是当前问题的核心。
内容的提问来源于stack exchange,提问作者plasmas222
相关产品推荐
相关产品推荐

