多边形游戏地图的生成 #2

作者:indienova
2016-05-26
13 33 2

引言

Amit 对多边形地图生成也颇有研究,本系列教程是其研究成果的结晶。
这里的 indienova 译文版参考了 ivan 博客的许多内容,他还非常慷慨地分享了自己制作的多边形地图生成器 unity 实现 nmap,在此特意表示感谢。
由于原文篇幅较长,分为多篇推出,本教程是该系列中的第二篇。本文中提到的 demo 参见系列文章第一篇。

ivan 的 nmap github 地址:https://github.com/losetear/nmap

河流

河流与湖泊是我需要的两种主要的淡水资源。一般实践中采用的方案是包含风,云,湿度,降水等属性的湿度图,并以降水量为基础来定义河流与湖泊。但这里我先设定河流位置,再逆推其他的属性。

岛屿轮廓划分水域与陆地的界限。湖泊即非海洋的水域。

河流使用上文提到过的下坡方向来定义。我会随机选择山脉多边形的一个顶角,然后按 Corner.downslope 的路径一路走向海洋。河流会从一个顶角流向另一个顶角:

river

我分别试过多边形中心与顶角这两种计算方式后,发现还是使用顶角图来生成的河流更好看。此外,令湖泊高度保持平坦,湖泊附件的地域自然海拔高度会更低,这样河流自然而然就会汇入和流出湖泊。多条河流间可以分享下游路径。每当河流流经某条边,我就会将存储在 Edge.river 中的水量 +1。在渲染时,将水量的平方表示为河流宽度的宽度,这种简单的方法能取得十分不错的效果。

湿度图

由于我采用逆推的方法,所以不需要使用河流来计算湿度。然而,在定义 生态群落 (沙漠,沼泽,森林等)时,湿度是非常必要的数据。水源理应出现在湿度较高的地区,因此我将湿度定义为随着远离淡水源头而逐渐减少。 我们令 Corner.moistureak,且 a < 1(例如取0.95),其中 k 表示与水源的距离。不幸的是,为了让生成的地图看起来合情合理,我还需要手动调整函数 Map.assignCornerMoisture 的参数:

moisture

类似对高度图的处理,我也重新分配了湿度知道它符合期望的分布。在本例中,我希望干湿地区大小大致相当。因此所需的累计分布函数是 y(x)=x,因此,调整分布的代码并不复杂。我先将顶角按湿度排序,然后按照顶角在排序后列表中的顺序重新分配它们的湿度值。你可以参考 Map.redistributeMoisture 查看相关的代码。

在地图生成器中,湿度只用于生态群落。然而,游戏设计中它还有许多其他可能的用途。例如,在《疯神国度》(Realm of the Mad God)中,我使用湿度和海拔来确定生成哪些植被和何种怪物。

生态群落

高度图和湿度图提供的数据为如何定义不同类型的生态群落提供了绝佳参考。我使用海拔高度来生成温度。如果所在地并非大陆,则维度会成为影响温度的重要因素。此外,风向,水分蒸发量,雨影区域也都会影响湿度的分布。但在本例编写的生成器中,我尽量让一切保持简单。生态群落的属性主要取决于它所处位置是水域还是陆地:

  • 海洋是所有直接与地图边界相连的水域格子;
  • 湖泊是所有不直接与地图边界相连的水域格子:特别地,如果处于高海拔地区,属性为冰湖,如果处于低海拔地区,属性则为沼泽
  • 沙滩是与海洋相连的陆地格子。

对陆地多边形而言,我最初使用 Whittaker diagram,随后将其适当修改以适应我的需求:

海拔
高度
湿度分布
6
(湿)
5 4 3 2 1
(干)
4
(高)
雪地(SNOW) 苔原(TUNDRA) 荒原(BARE) 焦土(SCORCHED)
3 针叶林(TAIGA) 灌木丛(SHRUBLAND) 高寒荒漠(TEMPERATE DESERT)
2 温带雨林(TEMPERATE RAIN FOREST) 温带落叶林(TEMPERATE DECIDUOUS FOREST) 草原(GRASSLAND) 高寒荒漠(TEMPERATE DESERT)
1
(低)
热带雨林(TROPICAL RAIN FOREST) 热带季雨林(TROPICAL SEASONAL FOREST) 草地(GRASSLAND) 亚热带荒漠(SUBTROPICAL DESERT)

结果如下:

biomes

地图生成器demo中生成的生态群落效果十分不错,但每个游戏会有自己独特的需求。以《疯神国度》为例,就没有使用这些生物群落,而是采用了基于海拔和湿度的自己的算法。

对边的噪音化处理

有些游戏中,使用多边形地图就足以满足要求,然而有些游戏却希望不暴露多边形的结构。我一般使用的方法是,用噪音化处理后的线条来代替多边形的轮廓。如果我本来就想隐藏掉它,为什么还需要大费周折地使用多边形的结构呢?因为这种隐藏起来的结构能让我更方便地设计游戏机制,使用成熟的寻路算法。

回想前面我们曾提过的两种拓扑图:一个使用泰森多边形顶角(即下图中的1, 2)和边(蓝色),而另一个则使用多边形的中心(A, B)和作为中心连线的德劳内三角边(红色):

edge-duality

我希望对各种类型的边进行噪音化处理,同时使其不至于穿过其他多边形。在使得其线条足够随机性时,我也希望生成的形状看上去更加切合实际。我注意到点 A, 1, B2 构成了一个四边形,我可以将线条的扭曲变形限定在这个四边形的范围内:

edge-noisiness

我更进一步将这个四边形划分为四个小四边形。有两个用于红色的德劳内三角边,而另外两个用于泰森多边形边。只要保证这些线条于其中心汇聚,并且待在为它们预留好的范围内,它们就永远不可能相互交叉。这种做法很好地限制了它们的变形范围。需要小心的是,四边形未必会是凸四边形,为了保证将其划分得当,我选择将从泰森多边形边的中点作为分割点,而没有使用泰森多边形边和德劳内三角边的交叉点。

整个地图都能被划进四边形区域,无一遗漏:

quad-markings

这能够保证噪音化处理后的线条不受不必要的限制。我决定递归地细分四边形,并将小四边形内的线段连接成完整的轮廓。算法代码包含在 NoisyEdge.asbuildNoisyLineSegments 中。其最终结果是多边形轮廓不再是刻板的笔直线段:

biomes-noisy

有三处需要仔细微调噪音化参数:

  • 当划分的四边形小于预设数值时,递归函数就会中止。在范例中,我分别使用过大小为7,4和1的四边形片段。在地图 demo 中,大小为1的线段用于河流和海岸线,3用于生态群落的边界,而10用于其他地方。
  • 为红色四边形(德劳内三角边)和蓝色四边形(泰森多边形边)分配空间需要权衡折中,我将 NoisyEdges.NOISY_LINE_TRADEOFF 设定为 0.5
  • NosiyEdges.subdivide 方法中会设定一个随机数范围,目前版本的 demo 设定的范围为 0.2-0.8,但设定成 0.0-1.0 也不成问题。此外,随机数不必遵从线性分布。如果你避免空间被平均分割,可能会产生更明显的噪音化视觉效果。

对多边形的边(尤其是河流,海岸线等)进行噪音化处理被证明会对所生成的地图外观产生重大的影响。

更多的噪音化处理

我对在游戏创作中应用噪音处理方法一直抱有巨大的兴趣,因此也想对生成的地图应用一点类似的技巧。一般在真正的游戏地图中,噪音化处理用来生成植被,或者对地形进行细微的调整。在 demo 中,我在屏幕上覆盖了一层噪点位图来制造随机噪点材质的效果。我也运用多阶混色的技巧来柔化相邻多边形的边界:

voronoi-map-goal

下图渲染了 16,000 个多边形,包含噪音化处理后的边和一个噪点材质叠加图层,并添加了简单的光照效果:

voronoi-map-goal-16000-shaded

生态群落变迁的平滑处理

另外一种在多边形格子边界处混合处理生态群落的方法是:利用顶角的高度和湿度数值来构建一个渐变的梯度数值,然后逐像素地指定生态群落属性:

voronoi-map-goal-smooth

如果游戏机制不需要整个多边形格子都必须同处于同一种生态群落,那么这个方法就可以用来生成更富有趣味性的地形边界。

生态群落变迁的扭曲处理

对高度图和湿度图进行扭曲处理,也是一种让地图外观更加没有多边形痕迹的方法:

  1. 对高度图和湿度图的每个像素位置都使用培林或随机噪音化处理;
  2. 对附近的点取样,使用培林或者随机噪音函数来改变坐标位置。

下例展示了这样做取得的效果:

voronoi-map-goal-distorted

对高度图和湿度图进行噪音化处理,会在不同区域交界的边缘产生“抖动”效果;而对附近的点取样并进行噪音化处理,则会产生让地形轮廓形状出现自然扭曲的效果。

题图说明

题图使用的随机世界地图是使用免费工具 http://donjon.bin.sh/world/ 生成的,这是一个非常有趣的工具站点。

近期点赞的会员

 分享这篇文章

indienova 

indienova - 独立精神 

您可能还会对这些文章感兴趣

参与此文章的讨论

  1. 至尊小夜猫 2016-05-30

    有点像最早的3D引擎原理,通过这种独特的分割来达到良好的效果。

  2. 79nos 2018-01-05

    这个湿度+高度控制区域类型的方法效果不错啊,我以前只想到用湿度控制

您需要登录或者注册后才能发表评论

登录/注册