unity中怎么做河流_【(原创)为Unity3D游戏生成2D和3D多边形随机地图(1/3) unity3d 随机移动 unity3d 加入河流 unity3d如何随机出现 unity3d中怎么加入河...

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有帐号?注册帐号

9f547f370dcc586bae5ddae68dee88bc.gif

x

本帖最后由 小刺刺 于 2015-6-26 17:10 编辑

这是一篇关于如何生成自定义随机地图的文章,可以生成2D或者3D数据,包含山峰、湖泊、河流、草地等,相对于噪声算法生成的随机地图,这篇文章提供的思路拥有更多的控制权,可以针对你的游戏策划案来做相应的调整。由于工作繁忙,所以将这片翻译拆成三部分来发,敬请谅解。后面会提供对应的u3d工程直接使用。

因能力有限,翻译不到位的地方请多多原谅。

Ivan.Z为游戏生成多边形地图

4 Sep 2010

I wanted to generate interesting game maps that weren’t constrained to be realistic, and I wanted to try some techniques I hadn’t tried before. I usually make tile maps but instead used a different structure. What could I do with 1,000 polygons instead of 1,000,000 tiles? The distinct player-recognizable areas might be useful for gameplay: locations of towns, places to quest, territory to conquer or settle, landmarks, pathfinding waypoints, difficulty zones, etc. I generated maps with polygons, then rasterized them into tile maps that looked like this:

2010年9月4日

我想生成一些有趣的不被现实所束缚的游戏地图,并且想尝试一些新技术。我通常用不同的结构来创建tile map。我怎样才能只使用1000个多边形而不是1百万个tile?对于不同的玩家来说,可识别的游戏区域对可玩性更有帮助:城镇地区,任务地点,可征服或定居的领地,地标,寻路坐标,难度标示区域等。我生成了多边形地图,然后栅格化成tile map,参考下图:

Most procedural map generators, including some of my own previous projects, use noise functions (midpoint displacement, fractal, diamond-square, perlin noise, etc.) to generate a height map. I did not do that here. Instead, I used a graph structure to model the things directed by gameplay constraints (elevation, roads, river flow, quest locations, monster types) and noise functions to model the variety not constrained by gameplay (coastline shape, river placement, tree placement).

大多数程序生成的地图,包括一些我以前的项目,都是使用噪声函数(midpoint displacement, fractal, diamond-square, perlin noise等)来生成一张高度图。在这里我并没有这样做。相反,我用图结构来建模一些被游戏玩法所限制的东西(海拔,道路,河流,任务地点,怪物种类)并用噪声函数将一些不被游戏所限制(比如海岸线形状,河流的位置,树的位置)的变量建模。

There were three main things I wanted for this project: good coastlines, mountains, and rivers. For the coastline, I wanted to make island maps that are surrounded by ocean, so that I don’t have to deal with people walking to the edge of the map. For the mountains, I started with something simple: mountains are whatever’s farthest from the coastline, so that you can always walk uphill to reach the top. For the rivers, I started with something simple: draw rivers from the coast to the mountains, so that you can always follow rivers down to the beach.

我为这个项目定了三个主要的目标:良好的海岸线,山脉,以及河流。对于海岸线,我想创建一个被海洋包围的岛屿地图,这样我就不用处理人物走到地图边缘的情况了。对于山峰,我开始用一些简单的处理:山不管什么情况下都是离海岸线最远的,这样你就可以永远沿着上坡到达顶峰。对于河流,同样简单处理:河流源于海岸终于山峰,这样你就可以始终遵循河流到海滩。

First, try the demo! (Flash) Read on to learn how it works, or get the source code. Here’s the overview of the process:

首先, 尝试Demo ! (注意)请仔细阅读,了解它是如何工作的,或者获取并查看源代码 。下面是这个过程的描述:

Every project will have its own gameplay constraints. For this project, the gameplay constraints were partially taken from Realm of the Mad God, a multiplayer RPG in which players start on the beach playing alone and then later join together on the mountaintop to fight bosses. Elevation directly corresponds to difficulty, and must monotonically increase, so that was a key constraint in the design. Elevation in Minecraft on the other hand isn’t constrained the same way, so the noise function they use works for that game. In multiplayer Age of Empires, the location of resources is constrained by the need to be somewhat balanced among the players; in Minecraft the distribution of resources is not constrained. When writing your own map generator, think about what which aspects of your map are set by the design and which can vary from map to map. Each of the ideas on this page can be used separately or together in your own map generator project.

每个项目都会有自己的游戏玩法限制。对于这个项目,玩法的约束部分取自Realm of the Mad God ,这是一个多人RPG,玩家一开始独自在海滩上玩耍,之后汇合其他玩家一起到山顶打Boss。海拔和难度是直接对应的,并且必须单调增加,所以这是游戏设计中的一个关键约束。海拔在Minecraft游戏拥有另一方面的约束,所以噪声函数在这个游戏里运作的很好。在多人帝国时代里,资源的位置是由需要约束为玩家之间有所平衡;在的Minecraft资源分配是不是限制。当你开始写自己的地图生成器的时候,需要考虑那些方面是跟随设计而定的,那些是跟随着地图变化的。此页面上的每个想法都可以单独或一起用在你的地图生成器里。

Polygons多边形

The first step is to generate some polygons. The simplest approach would be to use a hexagonal grid and perturb it a bit to make it look irregular. This works (and the techniques on this page will work if you use a perturbed grid), but I wanted something even less regular than that, so I picked random points and generated Voronoi polygons, which are used for lots of things, including maps. The Voronoi wiki is incomplete but has some useful background. I’m using nodename’s as3delaunay library, which has an implementation of Fortune’s Algorithm.

第一个步骤是生成一些多边形。最简单的方法是使用一个六边形网格,然后将之扰乱使它看起来不规则。此方法可行(如果你使用一个扰乱后的网格也可以配合本页的技术使用),但我想要一个更不规则的东西,所以我选择了随机点来生成的Voronoi多边形 ,它被用于很多事情 ,包括地图。该沃罗诺伊 维基页是不完整的,但包含了一些有用的背景知识。我使用一个as3delaunay 库 ,它实现了Fortune’s Algorithm算法 。

Here’s an example of random dots (red) and the polygons that result:

下面是随机点(红色)和由此产生的多边形的一个例子:

The polygon shapes and sizes are a bit irregular. Random numbers are more “clumpy” than what people expect. I want something closer to semi-random “blue noise”, or quasirandomness, not random points. I approximate that by using a variant of Lloyd relaxation, which is a fairly simple tweak to the random point locations to make them more evenly distributed. Lloyd relaxation replaces each point by the centroid of the polygon. In my code I merely average the corners (see improveRandomPoints). Here’s the result after running approximate Lloyd relaxation twice:

多边形的形状和大小都有点不规则。随机数比人们预期的更加成“块状”分布。我想要它们变得更接近半随机的“蓝噪音”,或quasirandomness ,而不是全随机的点。我通过使用一种Lloyd relaxation的变种算法达到了近似的效果 ,它通过一个相当简单的调整,来使随机点的位置更加均匀地分布。Lloyd relaxation将每个点的坐标替换成多边形的质心。在我的代码,我只是平均了拐角的坐标(查看improveRandomPoints )。下面是运行Lloyd relaxation放松两次之后的结果:

Compare it to running once or fifty times. The more iterations, the more regular the polygons get. Running it twice gives me good results but every game will vary in its needs.

比较运行算法一次或50次的区别 。迭代越多,地图就会变得越来越规整。运行两次已经可以满足我们的需求,但每个游戏都会有自己不同的需求。

Polygon sizes are improved by moving polygon centers. The same approach works to improve edge lengths. Moving corners by averaging the nearby centers produces more uniform edge lengths, although it occasionally worsens the polygon sizes. In the code, see the improveCorners function. However, moving corners will lose the Voronoi diagram properties. Those properties aren’t used in this map generator, but keep this in mind if you want to use those properties in a game. You can either get better edge lengths or you can preserve the Voronoi distance properties.

多边形的大小通过移动多边形的中心来改善。同样的方法适用于更改边长。通过移动附近中心的角落来产生更多统一的边缘长度,虽然偶尔会增加多边形的大小。在代码中,查看improveCorners功能。然而,移动边角会丢失Voronoi图的属性。这些属性并未在本地图生成器中使用,请记住这点,以防但如果你想在游戏中使用这些属性,记住这一点。您可以选择得到更好的边长,或者保留Voronoi距离的特性。

Using Voronoi adds some complexity so if you want to start with something simpler, try a square or hexagonal grid (you can see this in the demo). The rest of the techniques in this article will work with a grid. Optionally, randomly perturb the vertices of the grid to make it a little more natural looking.

使用Voronoi增加了一些复杂度,所以如果你想尝试一些简单的东西,选择方形或六边形网格(你可以在Demo里看到)。本文剩下的技术讲述如何在网格下工作。可选,随机地扰乱网格的顶点,使其稍微更自然的。

Map Representation呈现地图

I’m representing the map as two related graphs: nodes and edges. The first graph has nodes for each polygon and edges between adjacent polygons. It represents the Delaunay triangulation, which is useful for anything involving adjacency (such as pathfinding). The second graph has nodes for each polygon corner and edges between corners. It contains the shapes of the Voronoi polygons. It’s useful for anything involving the shapes (such as rendering borders).

我用两个相关的图 :节点和边 来表示地图。第一个图拥有每个多边形的节点和相邻多边形之间的边缘。它代表了Delaunay三角形 ,这是一个用于处理任何涉及邻接的数据(例如寻路)。第二个图包含了每个多边形角落的节点和角落之间的边缘。它包含了Voronoi多边形的形状。这对任何涉及形状的处理(如渲染边界)是有用的。

The two graphs are related. Every triangle in the Delaunay triangulation corresponds to a polygon corner in the Voronoi diagram. Every polygon in the Voronoi diagram corresponds to a corner of a Delaunay triangle. Every edge in the Delaunay graph corresponds to an edge in the Voronoi graph. You can see this in the following diagram:

这两个图是相互关联的。每个Delaunay三角里的三角形都对应于Voronoi图的多边形边缘。Voronoi图里的每个多边形都对应于Delaunay三角形的所有边。Delaunay图中的每个边缘都对应于Voronoi图中的一条边。你可以在下图中看到对应关系:

Polygon A and B are adjacent to each other, so there is a (red) edge between A and B in the adjacency graph. For them to be adjacent there must be a polygon edge between them. The (blue) polygon edge connects corners 1 and 2 in the Voronoi shape graph. Every edge in the adjacency graph corresponds to exactly one edge in the shape graph.

多边形A和B彼此相邻,所以有一条边缘(红)在A和B之间。因为它们是相邻的,所以肯定有一条多边形边在二者之间。多边形边(蓝色)连接了Voronoi图中的拐角1和拐角2。邻接图的每条边都对应于形状图的边缘。

In the Delaunay triangulation, triangle A-B-C connects the three polygons, and can be represented by corner 2. Thus, corners in the Delaunay triangulation are polygons in the Voronoi diagram, and vice versa. Here’s a larger example showing the relationship, with Voronoi polygon centers in red and corners in blue, and the Voronoi edges in white and the Delaunay triangulation in black:

在Delaunay三角图里,三角形A - B - C连接三个多边形,同时这几个多边形可以用拐角2 表示。因此,在Delaunay三角里的拐角也可以表示Voronoi图里的多边形,反之亦然。这里用一个更大的例子来展示这种关系,Voronoi多边形的中心点用红色表示,拐角用蓝色显示,以及用白色表示Voronoi图的边缘和黑色表示Delaunay三角:

This duality means that I can represent the two graphs together. There are several approaches for combining the data from the two graphs. In particular, edges can be shared. Each edge in a normal graph points to two nodes. Instead of representing two edges in the two graph separately, I made edges point to four nodes: two polygon centers and two corners. It turns out to be quite useful to connect the two graphs together.

这种双重性意味着我可以一起展示这两种图形。有好几种方法可以整合这两种图的数据。特别是 共享边缘 。正常图形里的每个边缘都用两个节点来表示。我用四个节点来表示一条边缘:2个多边形中心和两个角,从而取代两个图里分别独立的边,。结果证明了这是一种有效的连接两个图的方法。

With the combined representation, I can now use the Relationships Between Grid Parts sections of my article on grids. They’re not grids so I’m not assigning grid coordinates, but many of the algorithms that work on grids also work here, and the algorithms that work on graphs also work here (on either of the two graphs).

随着合并表示,现在我可以使用关于网格的文章里提到的网格之间的关系 。不过它们(译者:所用的两种图)不是网格,所以我不能使用网格坐标,但许多建立在网格上的算法还可以正常工作,建立在图上的算法也同样可以运行(在任意一个图里)。

In the code, the graph/ directory has three classes: Center, Corner, and Edge:Center.neighbors is a set of adjacent polygons

Center.borders is a set of bordering edges

Center.corners is a set of polygon corners

Edge.d0 and Edge.d1 are the polygons connected by the Delaunay edge

Edge.v0 and Edge.v1 are the corners connected by the Voronoi edge

Corner.touches is a set of polygons touching this corner

Corner.protrudes is a set of edges touching the corner

Corner.adjacent is a set of corners connected to this one

在代码中, graph/目录下有三个类: Center , Corner和Edge :Center.neighbors是一组相邻的多边形

Center.borders是一组相接的边缘

Center.corners是一组多边形的拐角

Edge.d0和Edge.d1是由Delaunay边连接的多边形

Edge.v0和Edge.v1是由Voronoi边缘相连的角

Corner.touches是一组多边形所涉及的所有拐角

Corner.protrudes是一组边缘所相接的角落

Corner.adjacent是一组连接到自身的拐角

Islands岛屿

The second step is to draw the coastline. I used a simple function to divide the world into land and water. There are many different ways to do this. You can plug in your own shapes (even if they come from pizza boxes), although that code is not included in the demo. The map generator works with any division of points, but it forces the outer layer of polygons to be ocean. Here’s an example that divides the world into land and water:

第二个步骤是绘制海岸线。我用一个简单的函数来把世界分割成海洋和陆地。有许多方法都可以做到这一点。你可以使用自己的形状来标记地图(就算使用披萨盒的形状都可以 ),不过演示不包含此代码。地图生成器可以任意划分点(应该是多边形吧),但它默认了多边形的最外边是海洋。下面是将一个将世界分为陆地和海洋的例子:

In the code, Map.as contains the core map generation code. The IslandFunction returns True if a position is land, and False for water. There are four island functions included in the demo:Radial uses sine waves to produce a round island

Perlin uses Perlin noise to control the shape

Square fills the entire map with land

Blob draws my blob logo

在代码中, Map.as包含了核心的地图生成代码。如果当前位置是陆地,则IslandFunction函数返回True ,否则False代表海水。Demo里包含了四种岛屿的形状函数:Radial采用正弦波算法产生一个圆形的岛屿

Perlin使用培林噪声算法控制形状

Square将整个地图标记为陆地

Blob绘制了我的博客logo

The code assigns water/land to both polygon centers and corners:Assign water/land to the corners by setting Corner.water based on the IslandFunction.

Assign water/land to the polygons by setting Center.water if some fraction of the corners have water set.

该代码同时识别了多边形中心和拐角的海洋陆/地属性:通过Island Function 函数来设置Corner.water属性,从而识别拐角是海洋还是陆地。

如果部分拐角有water集,则将多边形 的Center.water设置为海洋。

A simple flood fill starting from the border of the map can determine which water areas are oceans (connected to the border) and lakes (surrounded by land):

一个简单的洪水填充从地图的边界开始,这样就可以确定哪些水域是海洋(连接到边缘)或湖泊(被陆地包围):

In the code, the flood fill runs on the polygon centers, and then we can decide what happens to corners:Set Center.ocean for any polygon connected to the borders of the map through water polygons. If Center.water is set but .ocean is not, then it’s a lake.

Set Center.coast if the polygon is land but has an ocean border. Coastal areas will later get drawn as beaches.

Set Corner.ocean if the corner is surrounded by ocean polygons.

Set Corner.coast if the corner touches ocean and land polygons.

Reset Corner.water to be consistent with the surrounding area.

在代码中,洪水填补算法使用了多边形的中心,然后我们可以决定拐角是什么:将任何可以通过海水多边形连接到地图边界的多边形设置Center.ocean标记。如果Center.water被设置,但.ocean没有,那么它是一个湖泊。

如果多边形是陆地,但有一个海洋边界,则设置Center.coast。沿海地区稍后会当作海滩来绘制。

如果拐角被海洋多边形包围,则设置Corner.ocean。

如果拐角同时触及海洋和陆地多边形,则设置Corner.coast。

将Corner.water重置为和周边地区一致。

Elevation海拔

The most realistic approach would have been to define elevation first, and then define the coastline to be where the elevation reaches sea level. Instead, I’m starting with the goal, which is a good coastline, and working backwards from there. I set elevation to be the distance from the coast. I originally tried elevations at polygon centers but setting elevations at corners worked out better. Corner-to-corner edges can serve as ridges and valleys. After calculating the elevation of corners (Corner.elevation), the polygon elevation (Center.elevation) is the average of the elevation at the corners. See the functions Map.assignCornerElevations and Map.assignPolygonElevations.

最常用的办法是先定义海拔图,然后将海拔达到海平面的地方定义为海岸线。相反,我从定义海岸线开始,然后倒退计算海拔。将海拔定义为离海岸的距离 。我本来试图将海拔设置在多边形的中心,但是发现设置在拐角处效果更好。角和角之间的边界线可以作为山谷。计算拐角的海拔(Corner.elevation )后,多边形的海拔高度( Center.elevation )就可以从所有拐角的平均值获得。请查阅函数Map.assignCornerElevations和Map.assignPolygonElevations 。

Water polygons don’t count towards the distance. This is both because I expect lakes to be flat instead of sloped, and because this tends to build valleys around lakes, which helps guide rivers towards lakes.

被标记为水面的多边形不算入计算海拔的多边形里面。这既是因为我期望湖泊是平坦而不是倾斜的,还因为我们的山谷是围绕着湖泊的,这有助于将河流导向湖泊。

One problem with the simple definition is that some islands have too many mountains and others have too few. To fix this, I redistribute the elevations to match a desired distribution, which has more low elevation land (coastline) than high elevation land (mountains). First, I sort the corners by elevation, and then I reset the elevation x of each to match the inverse of the desired cumulative distribution: y(x) = 1 - (1-x)^2. In the Map.redistributeElevations function, y is the position in the sorted list, and x is the desired elevation. Using the quadratic formula, I can solve for x. This preserves ordering so that elevations always increase from the coast to the mountains.

还有一个简单的问题,那就是一些岛屿拥有太多的山峰而其他的太少。为了解决这个问题,我将重新分配海拔来达到期望的分布,它将拥有更多的低海拔的土地(岸线)和少量的高海拔地(山)。首先,我通过海拔的高低来对拐角进行排序,然后我通过排序后海拔的索引x来达到期望的分布,新的海拔的值来自函数: y(x) = 1 - (1-x)^2 。在Map.redistributeElevations函数里, y是在排序列表中的位置,x是期望的海拔。使用二次方程式,我可以得到x 。通过保留排序,所以海拔总是从沿海到山峰增加。

For any location, going downhill will eventually lead to the ocean. This diagram shows the steepest downhill direction from every corner, stored in Corner.downslope:

对于任何位置,一直下坡都会到达海洋。此图显示了从每一个角落的下坡方向,存储在Corner.downslope :

By following the downhill arrows from any location, we eventually reach the ocean. This will be useful for rivers but may also be useful for calculating watersheds and other features.

按照任何位置的下坡箭头,我们都能到达海洋。这对于计算河流很有用,但也可以用于计算分水岭和其它特征。

I had two main goals for elevation:Biome types: high elevations get snow, rock, tundra; medium elevations get shrubs, deserts, forests, and grassland; low elevations get rain forests, grassland, and beaches.

Rivers flow from high elevations down to the coast. Having elevations that always increase away from the coast means that there’s no local minima that complicate river generation.

对于海拔,我有两个主要目标:生物群落类型:高海拔地区会下雪,岩石,苔原;中海拔获得灌木,沙漠,森林,草原,低海拔获得雨林,草原,和海滩。

河流从高处流到海岸。其海拔总是和海岸的距离相反,意味着不会有局部的小且复杂的河流产生。

In addition, games may define their own use of elevation data. For example, Realm of the Mad God uses elevation to distribute monsters.

此外,游戏可以定义自己的海拔数据。例如, Realm of the Mad God利用海拔来分布怪物。

This elevation calculation works for simple islands, which is what I needed for Realm of the Mad God. For continent generation, you’ll want to change this step to generate one or more mountain ranges that aren’t necessarily in the center, as well as isolated volcanos.

这种海拔算法适用于简单的岛屿,正是我需要为Realm of the Mad God 准备的。对于生成大陆,你可能想要改变这个算法,来生成一个或多个不在中心的山脉,或者孤立的火山。

unity3d 随机移动;unity3d 加入河流;unity3d如何随机出现;unity3d中怎么加入河流

你可能感兴趣的:(unity中怎么做河流)