在游戏开发中,手动建模往往是最耗时的环节之一。无论是简单的碰撞体、地形块还是特效网格,传统工作流程都需要在3D建模软件中创建,再导入Unity进行调试。这种反复切换工具的过程不仅效率低下,还容易产生版本混乱。本文将介绍如何通过UnityEditor脚本快速生成自定义Mesh并直接保存为项目资源,彻底改变你的建模工作流。
程序化生成网格的核心价值在于可重复性和参数化控制。想象一下,当你需要创建数百个不同尺寸的碰撞体时,手动建模几乎是不可能完成的任务。而通过脚本生成,只需调整几个参数就能批量产出。
典型应用场景包括:
提示:编辑器脚本生成的Mesh资源可以直接在Prefab或场景中引用,与常规建模导入的FBX文件使用方式完全相同
理解Mesh的底层数据结构是编写生成脚本的前提。一个完整的Mesh主要由以下核心元素构成:
csharp复制// 顶点坐标数组
Vector3[] vertices = new Vector3[4];
vertices[0] = new Vector3(-1, 0, -1); // 左下
vertices[1] = new Vector3(-1, 0, 1); // 左上
vertices[2] = new Vector3(1, 0, 1); // 右上
vertices[3] = new Vector3(1, 0, -1); // 右下
// 三角形索引数组
int[] triangles = new int[6] {
0, 1, 2, // 第一个三角形
0, 2, 3 // 第二个三角形
};
关键概念解析:
| 术语 | 说明 | 注意事项 |
|---|---|---|
| 顶点(Vertex) | 3D空间中的坐标点 | 顺序影响法线方向 |
| 三角形(Triangle) | 由三个顶点索引定义的面 | 必须按顺时针或逆时针统一 |
| UV坐标 | 纹理映射坐标 | 简单网格可暂不设置 |
| 法线(Normal) | 面的朝向向量 | 未设置时会自动计算 |
下面我们通过一个完整示例演示如何创建并保存四边形网格:
csharp复制using UnityEditor;
using UnityEngine;
using System.IO;
public class MeshCreator : EditorWindow
{
[MenuItem("Tools/Create Sample Mesh")]
static void CreateQuadMesh()
{
// 1. 定义网格尺寸
float width = 10f;
float height = 5f;
// 2. 初始化顶点数组
Vector3[] vertices = new Vector3[4];
float halfWidth = width * 0.5f;
float halfHeight = height * 0.5f;
vertices[0] = new Vector3(-halfWidth, 0, -halfHeight);
vertices[1] = new Vector3(-halfWidth, 0, halfHeight);
vertices[2] = new Vector3(halfWidth, 0, halfHeight);
vertices[3] = new Vector3(halfWidth, 0, -halfHeight);
// 3. 定义三角形连接顺序
int[] triangles = new int[6] {
0, 1, 2,
0, 2, 3
};
// 4. 创建Mesh对象并赋值
Mesh quadMesh = new Mesh();
quadMesh.vertices = vertices;
quadMesh.triangles = triangles;
quadMesh.RecalculateNormals(); // 自动计算法线
// 5. 保存为Asset文件
string savePath = "Assets/Resources/QuadMesh.asset";
AssetDatabase.CreateAsset(quadMesh, savePath);
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log($"Mesh已保存至: {savePath}");
}
}
操作步骤说明:
基础四边形只是起点,下面介绍几种提升Mesh生成效率的进阶方法:
将尺寸、分段数等参数暴露给Inspector,实现可视化配置:
csharp复制[SerializeField] private float meshWidth = 5f;
[SerializeField] private float meshHeight = 3f;
[SerializeField] private int widthSegments = 4;
[SerializeField] private int heightSegments = 2;
private void GenerateParametricMesh()
{
// 根据参数动态计算顶点数量和位置
int vertexCount = (widthSegments + 1) * (heightSegments + 1);
Vector3[] vertices = new Vector3[vertexCount];
// 分段生成顶点坐标
for(int x = 0; x <= widthSegments; x++) {
for(int y = 0; y <= heightSegments; y++) {
float xPos = Mathf.Lerp(-meshWidth/2, meshWidth/2, (float)x/widthSegments);
float zPos = Mathf.Lerp(-meshHeight/2, meshHeight/2, (float)y/heightSegments);
vertices[x * (heightSegments + 1) + y] = new Vector3(xPos, 0, zPos);
}
}
// 后续三角形生成和保存逻辑...
}
关键差异总结:
| 特性 | 编辑器模式 | 运行时模式 |
|---|---|---|
| 资源保存 | 可持久化到项目 | 仅内存中存在 |
| 性能影响 | 无运行时开销 | 每帧计算消耗CPU |
| 使用场景 | 预制资源生成 | 动态地形/破坏效果 |
| 访问权限 | 完整AssetDatabase API | 受限的文件系统访问 |
注意:运行时生成的Mesh如需持久化,必须使用额外的序列化方案
顶点顺序错误:
csharp复制// 错误示例 - 三角形顶点顺序不一致
int[] wrongTriangles = new int[6] {
0, 1, 2,
2, 3, 0 // 正确的顺序应该是0,2,3
};
症状表现:
解决方案:
结合噪声算法生成随机地形是程序化Mesh的典型应用。以下示例展示如何创建基础高度图网格:
csharp复制private void GenerateTerrainChunk(int size, float noiseScale)
{
Vector3[] vertices = new Vector3[size * size];
int[] triangles = new int[(size-1)*(size-1)*6];
// 生成顶点
for(int x = 0; x < size; x++) {
for(int z = 0; z < size; z++) {
float y = Mathf.PerlinNoise(x * noiseScale, z * noiseScale) * 10f;
vertices[x * size + z] = new Vector3(x, y, z);
}
}
// 生成三角形
int triIndex = 0;
for(int x = 0; x < size-1; x++) {
for(int z = 0; z < size-1; z++) {
int vertexIndex = x * size + z;
triangles[triIndex++] = vertexIndex;
triangles[triIndex++] = vertexIndex + size;
triangles[triIndex++] = vertexIndex + 1;
triangles[triIndex++] = vertexIndex + 1;
triangles[triIndex++] = vertexIndex + size;
triangles[triIndex++] = vertexIndex + size + 1;
}
}
// 创建并保存Mesh
Mesh terrainMesh = new Mesh();
terrainMesh.vertices = vertices;
terrainMesh.triangles = triangles;
terrainMesh.RecalculateNormals();
string savePath = $"Assets/Terrain/TerrainChunk_{System.Guid.NewGuid()}.asset";
AssetDatabase.CreateAsset(terrainMesh, savePath);
}
优化建议:
在实际项目中,这种技术可以大幅减少美术资源制作量。曾经有个需要200个独特地形区块的项目,通过参数化生成脚本,最终只用了3种基础高度图变体就实现了所有需求。