Unity低多边形程序化浮空岛网格生成问题求助
Unity低多边形程序化浮空岛网格生成问题解决指南
你现在做低多边形浮空岛的思路是对的——基于球体修改,但代码里几个关键逻辑出了问题,导致形态不对,我帮你拆解下问题,再给你修正后的代码和其他可选方案。
一、你的代码核心问题分析
- 动态值滥用:你在计算顶点坐标时混用了
Time.time和Random.Range,这会导致每一帧顶点都在乱跳,网格根本没法稳定下来,更别说得到固定的曲面形态了 - 顶部扁平化逻辑错误:直接把上半部分顶点的
y设为0,会让所有顶部顶点都挤在同一平面,自然会出现渐变收缩的问题,应该是把顶部区域的顶点向一个平坦的面偏移,而不是直接归零 - 噪声应用逻辑混乱:你没有基于球体的原始顶点做偏移,而是直接修改坐标分量,导致球体的基础形态被破坏,噪声也起不到自然扰动的作用
- 底部顶点衔接差:最后一个顶点的设置太随意,没有和底部的环形顶点衔接,会导致底部网格出现破面
- 性能与同步问题:
Update里每次都新建Mesh对象,而且协程的顶点计算和Update的网格赋值不同步,容易出现异常
二、修正后的完整代码
下面是调整后的代码,我保留了你的球体基础框架,修复了上述问题,实现了顶部平坦、底部带自然噪声的低多边形浮空岛:
using System.Collections; using UnityEngine; public class FloatingIslandGenerator : MonoBehaviour { public float baseRadius = 3f; public float topFlatness = 1.2f; // 控制顶部平坦程度,值越大顶部越平 public float bottomNoiseStrength = 0.8f; // 底部噪声强度 public int longitudeSegments = 24; public int latitudeSegments = 16; private MeshFilter _meshFilter; private Mesh _mesh; void Start() { _meshFilter = GetComponent<MeshFilter>(); _mesh = new Mesh(); _meshFilter.mesh = _mesh; StartCoroutine(GenerateIslandMesh()); } IEnumerator GenerateIslandMesh() { #region 顶点计算 Vector3[] vertices = new Vector3[(longitudeSegments + 1) * latitudeSegments + 2]; float pi = Mathf.PI; float twoPi = pi * 2f; // 顶部顶点(保留,但后续会把上半部分顶点拉平) vertices[0] = Vector3.up * baseRadius; for (int lat = 0; lat < latitudeSegments; lat++) { // 计算当前纬度的角度 float latAngle = pi * (float)(lat + 1) / (latitudeSegments + 1); float sinLat = Mathf.Sin(latAngle); float cosLat = Mathf.Cos(latAngle); // 区分顶部平坦区域和底部噪声区域 bool isTopHalf = lat <= latitudeSegments / 2; for (int lon = 0; lon <= longitudeSegments; lon++) { float lonAngle = twoPi * (float)(lon == longitudeSegments ? 0 : lon) / longitudeSegments; float sinLon = Mathf.Sin(lonAngle); float cosLon = Mathf.Cos(lonAngle); // 基础球体顶点 Vector3 baseVertex = new Vector3(sinLat * cosLon, cosLat, sinLat * sinLon) * baseRadius; if (isTopHalf) { // 顶部区域:拉平处理,让y坐标趋近于顶部顶点的y值 float flatFactor = 1 - (float)lat / (latitudeSegments / 2); baseVertex.y = Mathf.Lerp(baseVertex.y, vertices[0].y, flatFactor * topFlatness); } else { // 底部区域:添加Perlin噪声扰动,用顶点的x/z作为噪声坐标(避免抖动) float noise = Mathf.PerlinNoise(baseVertex.x * 0.5f, baseVertex.z * 0.5f) * bottomNoiseStrength; // 只向下偏移,避免破坏浮空岛的浮空感 baseVertex.y -= noise; } vertices[lon + lat * (longitudeSegments + 1) + 1] = baseVertex; yield return null; // 协程分步生成,避免卡顿 } } // 底部顶点:和底部环形顶点衔接,取底部区域的平均y值 float bottomY = 0; int bottomStartIndex = (latitudeSegments - 1) * (longitudeSegments + 1) + 1; for (int lon = 0; lon <= longitudeSegments; lon++) { bottomY += vertices[bottomStartIndex + lon].y; } bottomY /= (longitudeSegments + 1); vertices[vertices.Length - 1] = new Vector3(0, bottomY, 0); #endregion #region 法线计算 Vector3[] normals = new Vector3[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { normals[i] = vertices[i].normalized; } #endregion #region UV计算(保留原逻辑,可后续调整适配纹理) Vector2[] uvs = new Vector2[vertices.Length]; uvs[0] = Vector2.up; uvs[uvs.Length - 1] = Vector2.zero; for (int lat = 0; lat < latitudeSegments; lat++) { for (int lon = 0; lon <= longitudeSegments; lon++) { uvs[lon + lat * (longitudeSegments + 1) + 1] = new Vector2((float)lon / longitudeSegments, 1f - (float)(lat + 1) / (latitudeSegments + 1)); } } #endregion #region 三角形索引计算 int[] triangles = new int[((longitudeSegments * 2) + (longitudeSegments * latitudeSegments * 2)) * 3]; int index = 0; // 顶部帽状三角面 for (int lon = 0; lon < longitudeSegments; lon++) { triangles[index++] = 0; triangles[index++] = lon + 1; triangles[index++] = lon + 2; } // 中间主体三角面 for (int lat = 0; lat < latitudeSegments - 1; lat++) { for (int lon = 0; lon < longitudeSegments; lon++) { int current = lon + lat * (longitudeSegments + 1) + 1; int nextRow = current + longitudeSegments + 1; triangles[index++] = current; triangles[index++] = nextRow; triangles[index++] = nextRow + 1; triangles[index++] = current; triangles[index++] = nextRow + 1; triangles[index++] = current + 1; } } // 底部帽状三角面 int bottomVertexIndex = vertices.Length - 1; for (int lon = 0; lon < longitudeSegments; lon++) { int current = bottomStartIndex + lon; int next = bottomStartIndex + lon + 1; triangles[index++] = bottomVertexIndex; triangles[index++] = next; triangles[index++] = current; } #endregion // 赋值并更新网格 _mesh.Clear(); _mesh.vertices = vertices; _mesh.normals = normals; _mesh.uv = uvs; _mesh.triangles = triangles; _mesh.RecalculateNormals(); _mesh.RecalculateBounds(); } }
代码关键修改点说明
- 移除动态干扰值:去掉了
Time.time和随机值,改用固定的Perlin噪声基于顶点坐标计算,保证网格形态稳定 - 顶部平坦化优化:用线性插值
Mathf.Lerp让顶部区域的顶点逐渐向顶部顶点的y值过渡,避免突然归零导致的收缩 - 噪声合理应用:底部区域只对y轴做向下的噪声偏移,基于顶点的x/z坐标计算噪声,保证扰动自然且不破坏整体形态
- 底部顶点衔接:计算底部环形顶点的平均y值来设置底部中心顶点,避免破面
- 性能优化:只创建一次Mesh对象,协程分步生成顶点避免卡顿,同步更新网格
三、其他可选算法思路
如果觉得基于球体修改的方式不够灵活,还可以试试这些方法:
- 平面抬升法:先创建一个圆形平面,然后对平面的顶点用Perlin噪声抬升高度,边缘逐渐降低,再给底部添加一些向下的扰动,最后添加侧面网格形成浮空岛
- Marching Cubes算法:通过定义3D噪声场,提取等值面来生成更自然的有机形态,适合生成复杂的浮空岛地形,但实现起来稍复杂
- 低多边形地形工具:直接使用Unity Asset Store里的低多边形地形生成插件,能快速生成各种风格的浮空岛,节省开发时间
内容的提问来源于stack exchange,提问作者good112233




