如何为实时生成楼梯的Unity脚本添加Mesh并优化代码?
Unity楼梯Mesh生成问题修复与优化
核心问题分析与修复
1. 修复Update()执行顺序
原代码中楼梯尺寸计算逻辑滞后于顶点和Mesh生成,导致每帧使用上一帧的旧数据。需将参数计算移至最前端:
void Update() { // 先计算台阶高度、深度等核心参数 float HOS = EndPoint.position.y - StartPoint.position.y; float LOS = EndPoint.position.z - StartPoint.position.z; float stepHeight = HOS / steps_in_flight; float stepDepth = LOS / steps_in_flight; CreateStair(stepHeight, stepDepth); CreateMesh(); UpdateMesh(); }
2. 修复顶点数组与生成逻辑
原代码存在顶点数组尺寸错误、循环条件混乱、缺少踏板顶点等问题。重新设计顶点生成逻辑,每个台阶包含6个顶点(垂直 riser 的4个+水平 tread 的2个):
void CreateStair(float stepHeight, float stepDepth) { // 每个台阶6个顶点,总顶点数为台阶数×6 vertices = new Vector3[steps_in_flight * 6]; float halfWidth = width / 2f; float xLeft = StartPoint.position.x + halfWidth; float xRight = StartPoint.position.x - halfWidth; for (int s = 0; s < steps_in_flight; s++) { int baseIndex = s * 6; float currentY = StartPoint.position.y + s * stepHeight; float nextY = currentY + stepHeight; float currentZ = StartPoint.position.z + s * stepDepth; float nextZ = currentZ + stepDepth; // 垂直面(riser)顶点 vertices[baseIndex] = new Vector3(xLeft, currentY, currentZ); vertices[baseIndex + 1] = new Vector3(xRight, currentY, currentZ); vertices[baseIndex + 2] = new Vector3(xLeft, nextY, currentZ); vertices[baseIndex + 3] = new Vector3(xRight, nextY, currentZ); // 水平面(tread)顶点 vertices[baseIndex + 4] = new Vector3(xLeft, nextY, nextZ); vertices[baseIndex + 5] = new Vector3(xRight, nextY, nextZ); } }
3. 修复三角面生成逻辑
原ChatGPT生成的三角面索引完全无效,需根据顶点顺序生成符合Unity顺时针法线规则的三角面:
void CreateMesh() { // 每个台阶包含4个三角面(riser2个+tread2个),每个三角面3个索引 triangles = new int[steps_in_flight * 12]; int triIndex = 0; for (int s = 0; s < steps_in_flight; s++) { int baseVert = s * 6; // 绘制垂直riser面 triangles[triIndex++] = baseVert; triangles[triIndex++] = baseVert + 1; triangles[triIndex++] = baseVert + 2; triangles[triIndex++] = baseVert + 1; triangles[triIndex++] = baseVert + 3; triangles[triIndex++] = baseVert + 2; // 绘制水平tread面 triangles[triIndex++] = baseVert + 2; triangles[triIndex++] = baseVert + 3; triangles[triIndex++] = baseVert + 4; triangles[triIndex++] = baseVert + 3; triangles[triIndex++] = baseVert + 5; triangles[triIndex++] = baseVert + 4; } }
4. 性能优化:避免每帧重建Mesh
原代码每帧强制重建Mesh,性能损耗极大。改为仅在参数或端点位置变化时才重新生成:
Vector3 lastStartPos; Vector3 lastEndPos; int lastSteps; float lastWidth; void Start() { mesh = new Mesh(); mesh.name = "StairMesh"; GetComponent<MeshFilter>().mesh = mesh; lastStartPos = StartPoint.position; lastEndPos = EndPoint.position; lastSteps = steps_in_flight; lastWidth = width; GenerateStair(); } void Update() { // 仅在参数变化时触发重建 if (StartPoint.position != lastStartPos || EndPoint.position != lastEndPos || steps_in_flight != lastSteps || width != lastWidth) { GenerateStair(); lastStartPos = StartPoint.position; lastEndPos = EndPoint.position; lastSteps = steps_in_flight; lastWidth = width; } } void GenerateStair() { float HOS = EndPoint.position.y - StartPoint.position.y; float LOS = EndPoint.position.z - StartPoint.position.z; float stepHeight = HOS / steps_in_flight; float stepDepth = LOS / steps_in_flight; CreateStair(stepHeight, stepDepth); CreateMesh(); UpdateMesh(); }
数学优化建议
1. 共享顶点减少冗余计算
将楼梯视为网格结构,相邻台阶共享顶点,总顶点数从6×台阶数降至2×(台阶数+1)²,大幅减少内存占用和计算量:
void CreateOptimizedStair(float stepHeight, float stepDepth) { int numY = steps_in_flight + 1; int numZ = steps_in_flight + 1; vertices = new Vector3[2 * numY * numZ]; int index = 0; for (int y = 0; y < numY; y++) { float currentY = StartPoint.position.y + y * stepHeight; for (int z = 0; z < numZ; z++) { float currentZ = StartPoint.position.z + z * stepDepth; vertices[index++] = new Vector3(StartPoint.position.x + width/2, currentY, currentZ); vertices[index++] = new Vector3(StartPoint.position.x - width/2, currentY, currentZ); } } }
2. 向量运算简化坐标计算
使用Unity内置Vector3运算替代单独计算x/y/z,代码更简洁高效:
Vector3 stepDir = (EndPoint.position - StartPoint.position) / steps_in_flight; Vector3 currentPos = StartPoint.position; for (int s = 0; s < steps_in_flight; s++) { Vector3 nextPos = currentPos + stepDir; // 基于currentPos和nextPos生成顶点 currentPos = nextPos; }
完整修正后的代码
Mesh mesh; public Vector3[] vertices; int[] triangles; public Transform StartPoint; public Transform EndPoint; public int steps_in_flight = 10; public float width = 10f; Vector3 lastStartPos; Vector3 lastEndPos; int lastSteps; float lastWidth; void Start() { mesh = new Mesh(); mesh.name = "StairMesh"; GetComponent<MeshFilter>().mesh = mesh; lastStartPos = StartPoint.position; lastEndPos = EndPoint.position; lastSteps = steps_in_flight; lastWidth = width; GenerateStair(); } void Update() { if (StartPoint.position != lastStartPos || EndPoint.position != lastEndPos || steps_in_flight != lastSteps || width != lastWidth) { GenerateStair(); lastStartPos = StartPoint.position; lastEndPos = EndPoint.position; lastSteps = steps_in_flight; lastWidth = width; } } void GenerateStair() { float HOS = EndPoint.position.y - StartPoint.position.y; float LOS = EndPoint.position.z - StartPoint.position.z; float stepHeight = HOS / steps_in_flight; float stepDepth = LOS / steps_in_flight; CreateStair(stepHeight, stepDepth); CreateMesh(); UpdateMesh(); } void CreateStair(float stepHeight, float stepDepth) { vertices = new Vector3[steps_in_flight * 6]; float halfWidth = width / 2f; float xLeft = StartPoint.position.x + halfWidth; float xRight = StartPoint.position.x - halfWidth; for (int s = 0; s < steps_in_flight; s++) { int baseIndex = s * 6; float currentY = StartPoint.position.y + s * stepHeight; float nextY = currentY + stepHeight; float currentZ = StartPoint.position.z + s * stepDepth; float nextZ = currentZ + stepDepth; // 垂直面顶点 vertices[baseIndex] = new Vector3(xLeft, currentY, currentZ); vertices[baseIndex + 1] = new Vector3(xRight, currentY, currentZ); vertices[baseIndex + 2] = new Vector3(xLeft, nextY, currentZ); vertices[baseIndex + 3] = new Vector3(xRight, nextY, currentZ); // 水平面顶点 vertices[baseIndex + 4] = new Vector3(xLeft, nextY, nextZ); vertices[baseIndex + 5] = new Vector3(xRight, nextY, nextZ); } } void CreateMesh() { triangles = new int[steps_in_flight * 12]; int triIndex = 0; for (int s = 0; s < steps_in_flight; s++) { int baseVert = s * 6; // 绘制垂直面 triangles[triIndex++] = baseVert; triangles[triIndex++] = baseVert + 1; triangles[triIndex++] = baseVert + 2; triangles[triIndex++] = baseVert + 1; triangles[triIndex++] = baseVert + 3; triangles[triIndex++] = baseVert + 2; // 绘制水平面 triangles[triIndex++] = baseVert + 2; triangles[triIndex++] = baseVert + 3; triangles[triIndex++] = baseVert + 4; triangles[triIndex++] = baseVert + 3; triangles[triIndex++] = baseVert + 5; triangles[triIndex++] = baseVert + 4; } } void UpdateMesh() { mesh.Clear(); mesh.vertices = vertices; mesh.triangles = triangles; mesh.RecalculateBounds(); mesh.RecalculateNormals(); } void OnDrawGizmos() { if (vertices == null) return; foreach (var vert in vertices) { Gizmos.DrawSphere(vert, 0.1f); } }
内容的提问来源于stack exchange,提问作者the one and only
相关产品推荐
相关产品推荐

