游戏邦在:
杂志专栏:
gamerboom.com订阅到鲜果订阅到抓虾google reader订阅到有道订阅到QQ邮箱订阅到帮看

《DwarfCorp》开发者分享地形生成制作经验

发布时间:2013-08-16 16:41:02 Tags:,,,,,

作者:Matthew Klingensmith

这次我要说的是地形生成,以及它如何影响玩法。但愿本文对那些想要研究程序地形生成的程序员有所帮助。

king(from gamasutra)

king(from gamasutra)

探索新大陆

矮人族的国家变得太拥紧了。古老的大陆失去往昔的美丽,矿山附近到处是贫民窟、垃圾堆和血汗工场。后来,国王Dunold不满四处迁徒的“矮人过剩”(特别是孤儿),而且他们没有地方工作。

baloongo(from gamasutra)

baloongo(from gamasutra)

幸运的是,心灵手巧的矮人制作了热气球。伟大的矮人冒险家克服了这个种族对陌生环境的天生恐惧感,坐上热气球开始了全球寻找新大陆的征程。受命于国王的矮人军团把“过剩的矮人”送到其他地方,同时带回珍贵的矿石和奇异的生物。他们找到新大陆后就宣布该地为自己的国家所有。

程序生成

《DwarfCorp》中的大陆是程序生成的。这意味着我们要使用一套规则(通常是随机的)来制作这些大陆上的各种地面、高山、村庄、湖泊、河流、山丘和王国等。也意味着你每一次玩《DwarfCorp》,见到的都是新生成的大陆。每一次游戏都有所不同(但一定程度上也有类似的地方)。不像其他许多游戏中的程序地形,我们的地形的大小是固定的,这样我们的大陆就具有预先储存的整体性特征(如侵蚀、长河、山脉)。这是非常类似于《军团要塞》或《狂神国度》中的系统,尽管没有它们的那么复杂,比较不同于《我的世界》(这款游戏有无限的但局部重复性高的地形)。现在,我们要谈谈生成这种地形的技术细节。我们的程序大概可以分成以下几类:断层、山丘、侵蚀、生态群落、地标和文明。

断层

faults(from gamasutra)

faults(from gamasutra)

为了产生大陆的基本轮廓,我们首先要确定大陆地面的固定宽度和高度(玩家可以在选项页面中控制大小)。然后,我们在地图边缘附近随机画线,然后再在地图中间画几条随机线,如此反复三四次。这个过程就是我们所谓的“断层”。它其实与地球上的自然断层没什么关系,只是我们对这个过程的命名。下一步是生成所谓的断层地图的“维诺图”(游戏邦注:这是一种把分间划分成多个区域的方法;首先在这个空间中设置几个点,根据距离各个点最近的距离划分出各个点所占领的区域;各个区域之间不重叠)。为此,我们只要计算与各个点最接近的断层线的距离,然后将其设置为各个点的值 。结果就叫作“断层地图”。之后我们使用Simplex噪声法对断层地图做随机扭曲。

山丘

height(from gamasutra)

height(from gamasutra)

对于程序生成的游戏,Perlin噪声法是最强大的工具之一。技术上说,这是指一种让电脑图像模拟现实表象的方法。Simplex噪声法的2D效果就是带有随机高低效果的“山丘”。为了使我们的Simplex噪声法更自然,我们要使用三个图层:大陆噪声、高山噪声和山丘噪声。大陆噪声的规模大,起伏小。高山噪声规模较小,起伏相当大。山丘噪声相当小,起伏一般。我们只要仔细查看地图上的各个元素,再添加大陆噪声、高山噪声和山丘噪声。结果就是所谓的“噪声地图”。我们可以调整噪声地图的值,它的最低值为0,最大值为1.

侵蚀

uneroded(from gamasutra)

uneroded(from gamasutra)

我们现在得到的是相当好看的大陆(如果我们把断层地图叠加到噪声地图)。半岛、海湾和湖泊看起来相当自然。然而,似乎太光滑平坦了。那是因为在现实世界中,地面通常是“不平坦”的,而这个特点我们还没有模拟出来。对这个特征的成形,影响最大的就是雨水侵蚀和风化。为了模拟雨水侵蚀,我们要模拟大雨滴落在地面上砸出一个个小坑的效果。至于雨滴的数量(比如5000),我们可以从现实世界中取样。如果我们取样的象素低于海平面,我们就继续取样。否则,我们就必须模拟雨滴。为了模拟一个雨滴,我们执行一个叫作Gradient Descent(梯度下降)的功能。首先,我们追踪储存在雨滴中的材料有多少(一开始是0)。然后,我们看看雨滴的周边,然后计算这个梯度的近似值(这是下降最急的方向)。

erosion(from gamasutra)

erosion(from gamasutra)

之后,我们沿着梯度移动雨滴,使用随机数量的材料。当雨滴到达新的目的地时,它会留下小小的、随机数量的材料,之后再捡起更大的、随机数量的材料。我们重复这个过程直到雨滴达到海平面,或用完固定数量的步骤。

witherosion(from gamasutra)

witherosion(from gamasutra)

经过这么雨滴后,我们得到的结果是一个侵蚀更加严重、有通道、有峡谷和峡湾的大陆。

河流

flooding(from gamasutra)

flooding(from gamasutra)

河流与雨水侵蚀的步骤差不多,但它们侵蚀的是更宽广的区域。当地形的体素值生成,河流中就充满奔流的水了(根据我们的经验,那会引发洪水。)

生物群落

biomes(from gamasutra)

biomes(from gamasutra)

“生物群落”形容的是不同类的地形和覆盖在地面上的植被。到目前为止,我们有的生物群落如下:草地、沙漠、森林、丛林、针叶林、苔原和地狱。根据降水量、温度、高度和与地标的距离,地形可以归类到不同的生物群落中。为了生成气温,我们现在只运用从北到南的梯度,先用perlin噪声混合,然后变形。同样的做法也可以产生降雨。在以后,这些部分还要根据真正的物理过程作修改,但现在它看起来已经不错了。各个生物群落储存着出现在地形表面(通常是3层或4层),在表面之下和接近海岸的地方的特征。生物群落还储存植被类型和地表植被。例如,森林这种生物群落的最上层是草地,中层是岩石、海岸是沙子。大型树木、灌木和长得很高的草也是常见的。相反的,沙漠这种生物群落的最上层是沙子,除了少量粗糙的灌木,就没有其他植被了。

洞穴和矿石

洞穴和矿石是在构建体素的过程中产生的。为了生成洞穴,我们只要使用3D Perlin噪声功能,然后取一个我们称为“洞穴”的随机阈值;如果是矿石,各种矿石都有它自己的阈值和大小。 我们想让这个排列得更紧密一些,使洞穴和矿石产生得更自然。

地标

大陆上还随机分布着几个“地标”。它们是只存在于一个地方的特殊建物(如小镇、火山),可以任意影响地形。

volcano4(from gamasutra)

volcano4(from gamasutra)

根据地标可以得出的信息有:围绕地标放置的一系列“样板地形”、相关的生物群落、大小和位置。

文明

civs(from gamasutra)

civs(from gamasutra)

文明就比较复杂了,因为王国兴起和衰落背后并不存在简单的物理过程。(不幸的是,我们的原型中还没有加入文明,不过很快就会有了)我们的解决办法是把模拟文明变成“细胞自动机”的问题。我们首先在大陆上随机放置城市和小镇。各个城市都有一个随机名称,各个文明也有。

这是制作地标的过程的一部分。各个城市都包含如下信息:人口、防御力、居民种族(如地精、精灵等)。一开始,所有城市的种族都是中性的(即未确定为何种族),等着“都城”把它变成自己的种族居住区。各个文明由连续的区域点组成的。我们给文明的各个区域点一个叫作“辉光”的东西(这直接取自我们之前做的一款叫作《Dots Game》的游戏)。所有点的辉光初始值都是0。每一步,都城会给它的周边区域产生辉光。当周边区域的辉光高于50%时,它就从中性种族变成给它辉光的都城的种族。

当辉光到达中性城市时,这个城市就立即转化成辉光源头的种族。各个非都城区域之后会把它的辉光随机分给它的周边。当两个文明的辉光相遇,就会导致“战争”。在周边入侵城市的情况下,周边的辉光会削减城市的防御力直到0。在周边对抗周边的情况下,双方都损失一定量的辉光,直到一方的辉光大于另一方的。在战争中具有最多辉光的种族占领双方领土。在平坦的地形或下坡的地方,辉光更容易分散出去;而遇到高山或河流的阻挡则更难。辉光遇水不扩散。所有文明都扩张后,文明中两个位置最接近的城市之间会随机形成道路。这是非常复杂的,但结果(经过许多步模拟)是一张好看的文明地图!在游戏中,文明将用于产生敌人和非玩家角色。例如,如果玩家在地精文明附近刷出,他可能会遇到一群前往殖民地打战或贸易的地精。如果他在精灵国和地精国之间的边境刷出,那就有可能目睹双方的战斗。(本文为游戏邦/gamerboom.com编译,拒绝任何不保留版权的转载,如需转载请联系:游戏邦

How we Generate Terrain in DwarfCorp

by Matthew Klingensmith

This update is going to be about terrain generation, and how it affects gameplay. We’re hoping it is useful to other programmers who want to explore procedural terrain generation.

The Unexplored Continent

The Dwarven motherland has become quite overcrowded. The great hill mines of the old lands have lost their pristine beauty, and have grown chock full of shantytowns, refuse dumps, and sweatshops. Lately, King Dunold has complained of the “excess Dwarves” (especially orphans) who migrate about from mine to mine, without any place to work.

Luckily, the ingenuity of Dwarven science has produced the air balloon, and great Dwarven explorers, overcoming their natural Dwarven agoraphobia have spread out across the world in search of new lands. Dwarf Corps, commissioned by the king to send those “excess Dwarves” somewhere else, and to bring back precious ores and strange creatures, have taken it upon themselves to chart these new lands and claim them for the motherland.

Procedural Generation

The continents in DwarfCorp are procedurally generated. This means a set of rules (often random) is used to create each landscape, mountain, valley, lake, river, bay, hill, and kingdom you will find in these lands. It also means that each time you play DwarfCorp, a new continent will be generated from scratch. It will be different (yet somehow more or less the same) every time. Unlike the procedural terrain in many other games, our terrain is fixed in size. This allows us to have global features on our continents (like erosion, very long rivers, and mountain chains) which get stored before play. This is very similar (though not quite as complicated) as the system in Dwarf Fortress or Realm of the Mad God (which have continents of a fixed size to explore) and less similar to Minecraft (which has infinite, but locally very repetitive terrain). Now, we’ll discuss some of the technical details of how the terrain is generated. Our procedure falls roughly into these categories: Faults, Hills, Erosion, Biomes, Landmarks, and Civilizations.

Faults

To generate a basic outline of our continent, we start by laying out a plot of land of a fixed width and height (the player can control these dimensions in the Options screen). Then, we randomly draw lines near the edges of the map, and throw in a few random lines in the middle of the map by first randomly sampling a point, and then doing a random walk of three or four steps. The result of this process is what we call “the faultlines”. It really has nothing at all to do with natural faults you might find on Earth, and more of just a catchall term we use for the process. The next step is to generate what’s called a “Voronoi Diagram” of the faultline map. To do this, we just compute the distance from every pixel to the nearest fault line, and set that as the value of each pixel. The result is called the “Fault Map”. We then apply random distortion to the Fault Map using Simplex Noise.

Hills

One of the most powerful tools in procedurally generated games is the use of Perlin (or in our case Simplex noise). Technically, this refers to a random, smooth function of one or more variables. The result of evaluating Simplex noise in 2D is a sort of “hilly” function with random highs and lows. To make our Simplex noise look more natural, we have three layers: Continent Noise, Mountain Noise, and Hill Noise. Continent noise occurs over huge scales and its magnitude is quite low. Mountain Noise occurs over smaller scales and has very high magnitude. Hill noise is quite small and is of medium magnitude. We simply go through each pixel in the map, and add together the Continent Noise, Mountain Noise, and Hill Noise. The result is called the “Noise Map.” Before moving on, we scale the Noise Map so that the lowest value is zero, and the highest value is one.

Erosion

What we have now (if we just pixelwise multiply the Fault Map by the Noise Map) is a pretty nice looking continent. It will have natural peninsulas, islands, bays, and lakes. However, it looks a bit too smooth and flat. That’s because in the real world, “nonsmooth” effects occur on the landscape which we haven’t simulated yet. One of the biggest such effects is erosion from rainfall, and weathering from other sources. To simulate rain erosion, we literally simulate large rain droplets falling on the landscape and carrying bits of dirt from one place to another. For some number of raindrops (say 5,000), we randomly sample pixels in the world. If the pixel we sample is below sea level, we just continue. Otherwise, we need to simulate a raindrop. To simulate a single raindrop, we perform what is called Gradient Descent. First, we keep track of how much material is stored in the raindrop (this starts at zero). Then, we look in the local neighborhood of the raindrop and compute an approximation of the gradient (which is the direction of steepest descent).

We then move the raindrop along the gradient, and take a random amount of material with us down the slope. When the raindrop arrives at a new destination, it leaves a small, random amount of material, and picks up a larger, random amount of material. We repeat this process until either the raindrop hits the sea level, or we run out of a fixed number of steps.

The result, after many many raindrops, is a more eroded continent, with channels, canyons, and fjords.

Rivers

Rivers work just like raindrops in the erosion step, but they erode a significantly wider area. When the actual voxels of the terrain are generated, rivers are filled with dynamic water (which in our experience, causes terrible flooding!)

Biomes

“Biomes” describe the different types of terrain and vegetation which cover the landscape. So far, we have the following Biomes: Grassland, Desert, Forest, Jungle, Taiga, Tundra and Hellish. The terrain is classified into one biome or another based on the rainfall, temperature, height, and distance to a landmark. To generate temperature, we for now just apply a gradient from north to south, mix in perlin noise, and distort it. To generate rainfall, we do the same thing. In the future these parts will be based on actual physical processes, but for now it seems to work fine. Each biome stores which blocks occur on the top surfaces of the terrain (usually 3 or 4 levels down), under the subsurface, and near the shore. Biomes also store which vegetation types and ground cover occur there. For instance, the Forest biome specifies that grass occurs at the top level, rock at the subsurface, and sand on the shores. It also specifies that large trees should be fairly common, as well as berry bushes and tall grass. The “desert” biome on the other hand specifies sand at the top level, and no vegetation whatsoever save for a few gnarled bushes.

Caves and Ores

Caves and ores are generated on-the-fly during construction of the terrain voxels. To generate caves, we just sample a 3D Perlin noise function, and threshold on an arbitrary value we call “caviness,” or in the case of ores, each ore has its own threshold and scale at which to generate patches of ore. We want to change this to line up more closely with the way caves and ores are naturally created.

Landmarks

Several “landmarks” are also placed randomly around the world. These are special features which only occur in one place (such as a town, or a volcano), and which can have arbitrary effects on the terrain.

A landmark contains a bit of information: a set of “template terrains” to place around the landmark (in the case of volcanoes, a caldera), an associated biome (in the case of volcanoes, Hellish), a size, and location rules.

Civilizations

Civilizations are a tricky bit, since there isn’t a simple physical process behind how kingdoms rise and fall. (Unfortunately, civilizations haven’t made it into our prototype yet, but they will be there soon!) Our solution is to turn simulating civilizations into a problem of “Cellular Automata”. We start by randomly placing cities and towns in the world. Each city is given a random name, as is each civilization.

This is done as part of the landmark process. We give each city a few bits of information: its population, its defensive strength, and its associated race (goblin, elf, etc.). In the beginning, all cities are of race “neutral” save for a “capital” assigned to each race. We assume each civilization is made up of a continuous area of pixels. We give each pixel of the civilization a quantity called “glow” (this comes directly from an earlier game we’ve worked on called “Dots Game”). All pixels begin with zero glow. At each step, Capitals pump out glow to their neighbors. When the glow of a neighbor pixel is above 50%, it switches sides from race “neutral” to the race that caused it to glow.

When glow reaches a neutral city, it is immediately converted to the race causing the glow. Each non-capital pixel then distributes its glow to its neighbors randomly. When the glow of two civilizations meet, it causes a “battle”. In the case of countryside entering a city, the glow of the countryside subtracts from the city’s defensive strength until the strength of the city is zero. In the case of countryside vs. countryside, both sides lose some amount of glow until one is greater than the other by a fixed amount. The race of the pixel with the most glow in the battle then occupies both cells. Glow also spreads more easily along flat terrain or downhill than it does up the sides of mountains or over rivers. It also simply does not spread over water. After all of the civilizations spread, roads are randomly created between each of the nearest cities in a civilization. This is all very complicated, but the result (after many many steps of simulation), is a cool-looking civilization map! In the game, civilizations will be used to generate enemies and non-player characters. If the player spawns near a goblin city, for instance, he or she will occasionally get groups of wandering goblins coming to the colony for war or trade. If he or she spawns near the border of goblins and elves, there is a higher likelihood of seeing goblin-on-elf battles. (source:gamasutra)


上一篇:

下一篇: