程序化地图生成技术详解
程序化地图生成技术详解
引言
在现代游戏开发中,程序化内容生成(Procedural Content Generation, PCG)是一项关键技术,它能够自动生成游戏内容,如关卡、地图、纹理等。这种方法不仅可以大大减少开发者的手动工作量,还能创造出几乎无限的变化,增强游戏的可重玩性。本文将深入探讨程序化地图生成的各种技术和算法。
什么是程序化地图生成
程序化地图生成是一种通过算法自动生成游戏地图的技术。与手工设计的地图相比,程序化生成的地图具有以下优势:
- 无限多样性:每次生成的地图都可以是独一无二的
- 节省开发时间:减少手动设计地图的工作量
- 小体积存储:只需要存储生成算法而非完整地图数据
- 动态适应性:可以根据玩家行为或技能水平调整地图难度
常见的程序化地图生成算法
随机数生成法
最简单的地图生成方法是使用随机数生成器。这种方法适用于生成完全随机的地形单元,如随机分布的树木、石头等。
Perlin噪声算法
Perlin噪声是一种梯度噪声函数,由Ken Perlin在1983年发明。它能产生更加自然的噪声模式,广泛应用于地形高度图生成。
具体算法实现
本文主要聚焦于perlin噪声算法的实现,分为以下若干步骤【TODO】:
1,简单创建perlin噪声并且将其显示出来
2,丰富噪声,添加参数
单一的柏林噪声难以模拟现实世界地形的多种多样,像山川,河流,盆地等,因此我们最终要做到的效果是构造不同的柏林噪声并将其叠加,其中最重要的三个参数就是octaves,persistance和lacunarity:
public static float[,] GenerateMap(int width, int height,float scale,float octaves,float persistance,float lacunarity) octaves ——
叠多少层:其实就是跑了几次,构造了几个perlin噪声
persistence —— 幅度衰减("持久度"),控制每个不同噪声之间的振幅差值,呈递减规律。
lacunarity —— 频率增长("间隙度"),控制每个不同噪声之间的频率差值,呈递增规律。
叠加出来的噪声地图我们还要将其归一化
noiseMap[x, y] = Mathf.InverseLerp(minNoiseHeight, maxNoiseHeight, noiseMap[x, y]);
3,添加随机偏移
仅仅到这一步是不够的,因为柏林噪声的生成结果是固定的,我们需要添加一些随机偏移,才能得到更真实的地形。就像好多游戏里的种子(seed)一样,我们每次生成地图时,给柏林噪声添加一个随机的偏移量,这样每次生成的地图都是唯一的。同时,下次生成时,如果种子相同,则生成的地图也是相同的。
System.Random prng = new System.Random(seed);
Vector2[] octavesOffset= new Vector2[octaves];
for(int i = 0; i < octaves; i++)
{
float offsetX = prng.Next(-100000, 100000)+offset.x;
float offsetY = prng.Next(-100000, 100000)+offset.y;
octavesOffset[i] = new Vector2(offsetX, offsetY);
}
下面展示了两个不同种子值生成的地图:
种子值:21312
种子值:99999
从上面的对比可以看出,由于种子值的不同,生成的地图有明显的差异。
4,将噪声图转化为颜色图
这一步其实比较简单,我们需要将0~1直接的噪声按区间转化为对应颜色色块,初步思想是设置一个浮点数数组叫做地形数组,从大到小存储每个地形的最高高度,然后在遍历噪声地图数组的时候,依次比较当前噪声和地形数组中的值,如果当前噪声小于某个地形高度,则将当前噪声映射为对应的颜色。
for (int i = 0; i < terrianTypes.Length; i++)
{
if (currentHeight <= terrianTypes[i].height)
{
colorMap[width * y + x] = terrianTypes[i].color;
break;
}
}
添加颜色的地图(调了好久的参数,现在看起来已经初具人形了)
5,将之前的噪声图转化为网格并显示
这一步是最重要也是最难的一步,需要完成从2D到3D的转化,令人激动的一刻。梳理一下大致思路,其实一句话概括就是把原来的高度数组拔地而起,将height可视化转化为高度。创建网格最重要的是获取三个变量,triangles,vertices,uvs。我们先将高度图上的点编号,从左到右从上到下,从零开始,vertices和uvs比较简单,vertices中每个顶点的三维坐标xyz,x和z保持与高度图的x和y一致,y为高度值。uvs中每个顶点的二维坐标xy,x和y保持与高度图的x和y一致。而triangles就需要一些计算了,我们可以先画个草图:
三角形网格计算
我们将顶点编号,可以发现矩形网格总数跟点数有一定关系,公式为矩形数=(点列数-1)(点行数-1),三角形数量=矩形数2。因此,我们只需要遍历高度图,将每个顶点编号依次写入vertices和uvs数组中,并且写入triangles数组中,这样,我们就可以得到一个完整的网格了。
6,添加LOD
LOD(Level Of Detail)即细节级别,是指地图中不同距离的物体显示的细节程度。比如,当玩家距离某个物体较近时,显示的细节会更高,当玩家距离物体较远时,显示的细节会更少。 我们添加LOD层级的思路是动态划分网格,本质上是对之前的地图数组进行不同步长(stride)的遍历,以达到将地图进行不同分辨率的划分的目的。
LOD图
如图所示,黄色代表低层的LOD,红色代表高层的LOD。