Procedural random polygon map generation

Original: https://www.jianshu.com/p/08e9b772964b

foreword

Recently, the team wanted to develop an open world game. This is a very interesting game concept. However, after referring to the settings of "The Legend of Zelda: Breath of the Wild", we found that the success of this game is largely due to the large amount of art and design. This is a very interesting continent, but our team has no way to manually build such a large scene, so I came up with a very natural idea: write a script that automatically generates a reasonable terrain.

At first I tried Perlin noise to directly generate a height map, but the resulting terrain was too extreme, and the development of the script also entered a deadlock, until I saw an article in 2010: Original address: http://www
-
cs - students.stanford.edu/~amitp/game-programming/polygon-map-generation

This article implements a demo of an automatic random polygon map on flash. I followed the idea of ​​this article and basically copied a version of unity3D. I may write an unreal version in the future.

Source code github address

renderings

Basic Polygon Cell Map

Since a polygon-based map is to be generated, the first step is of course to generate a two-dimensional graph filled with polygons. In terms of terminology, this graph is called a Voronoi Diagram, and I prefer to call it a cell map.

There are many principles of generating such graphics on the Internet. I refer to this article:

http://blog.csdn.net/k346k346/article/details/52244123

It should be noted that the graph generated by this method is too random, we need to make it look neater, this uses the relaxation algorithm called Lloyd, which is a clustering algorithm, simply speaking, after several Iteration, each iteration moves the center point of the Voronoi diagram to the center of the polygon, and recalculates the Voronoi diagram. After the actual experiment, it was found that 2 iterations can fully achieve the effect.

data structure

The VoronoiElement file in the source code saves the data structure, and most of them have comments. The Center class that needs to be explained represents the center point of the Voronoi diagram, which is the vertex of the Delaunay triangular network, and the Corner class represents the outer center of the triangle, and also It is the intersection point of the polygons in the figure. The purpose of doing this is actually to generate the information of two images, the triangular image and the polygonal image. In the later development, the triangular image can be used for pathfinding and the polygonal image is mainly used for rendering.

Differentiate between land and water

In the second step, we need to add the difference between continents and water to our map. There are many factors in the formation of surface water in nature, but we can only use random simulation when simulating.

The original article gave a lot of simulation ideas, so I used the Perlin noise method for simulation. First, calculate a Perlin noise value for each Corner point on the map, and set certain parameters to determine whether the noise value can make the Corner have properties of water. The way I just use is to compare (PerlinNoise) and (waterScale + waterScale * Distance (center of the map, the point)) size, so that the parameter value of waterScale can be modified to modify the amount of water generated by the map, and at the same time ensure that the edge of the map is more Water is easy to appear, making our map look like a continent on the sea (my default map edge is water).

After determining the attributes of the Corner point, we record the Center (that is, the polygon) connected to the water Corner as the water attribute, and the Center connected to it can be obtained through the touches list of the Corner.

Differentiate between oceans, inland lakes, and coastlines

First of all, we stipulate that the polygon bordering the map boundary is the ocean, all the water in contact with the ocean is the ocean, the land in contact with the ocean is the coastline, and the rest of the water is the lake.

Here, a seed filling algorithm is used for calculation. In fact, this is a breadth-first search strategy. We first add the Center at the edge of the map to a Queue queue, and then execute a loop until there are no remaining elements in the queue. Each time the loop takes out the Center at the end of the queue, check the adjacent polygons that are in direct contact with the Center (this can be found in the neighbors linked list). If the adjacent Center is water and not an ocean, it means that this is a Center that has not been processed yet. Set it as Ocean and join the queue, if the adjacent Center is not water, set it as Coast and join the queue.

After processing all the Centers, we only need to set the Corner of the ocean polygon as the ocean, and the Corner of the coastline polygon as the coastline.

 

give height

Next we add height elements to our map, making our terrain rough.

In order to make the terrain random but reasonable, we adopted such a strategy:

It makes sense that places near the coastline tend to be low and island centers tend to be high.

The implementation plan is also breadth-first search. We define an elevation attribute for each Corner, which represents the shortest distance from the coastline. Every time a Corner passes, a certain random value is added to this distance (the larger the range of this random value, the terrain will become more rugged).

After the search is over, we unify the elevation of each Corner to 0~1 as the height of this point, and the height of each Center is the average height of all corners of the polygon.

It should be noted that we need to perform an additional operation after unifying the height of the Corner. We need to calculate the direction of the maximum gradient of each Corner, which will prepare for the generation of rivers in the future.

 

river

In the previous step, we have calculated the direction of the largest gradient of each corner, so that our river generation is simple, we only need to give a few parameters: the minimum height of the river, and the range of the number of rivers. Then we randomly give the starting point of the river (make sure it is greater than the minimum height, and on land), and then let the river flow down according to the gradient, and each Corener that flows through is set as a river until it flows into a lake or ocean, or after a certain distance

 

  • At this point our map is pretty good, but for gameplay we still add two features to the polygon

Humidity and Biomes

We still use the opposite process to the natural world. We determine the humidity of the polygon by the distance from the polygon to the water source. The calculation scheme is basically the same as the height calculation.

 

With the humidity and height, we can simulate the types of plants on the terrain. Here we use such a chart for simulation:

Readers can modify the data to make the terrain drier or wetter

rendering

So far we have used graphical debugging to display, we also need to render a 3D mesh.

I used the Mesh rendering method that comes with Unity, add a MeshRenderer and MeshFilter components to a GameObject, modify the vertex and triangle properties of the mesh, and use all the Center and Corner as vertices. For each polygon, let the Center vertex and each One side forms a triangular mesh (here, note that the vertices of the triangles should be sorted clockwise to ensure that the normal direction is correct), and at the same time divide the mesh into 13 submesh (representing 13 kinds of communities), and determine the distribution of the triangle mesh according to the polygonal biome To which submesh (mesh.SetTriangles), assign 13 materials to MeshRenderer to complete the rendering.

end

At present, I only use solid colors to replace the materials of different communities. In the future, I will refine each material to achieve an effect similar to low-polygon rendering.

And there are a lot of noise and fusion calculations that have not been done, such as mixed boundaries.

The pictures in the steps in this article are a total of 500 polygons, and the final rendering has a total of 2000 polygons. It takes about 20s from compilation, startup, calculation to rendering. The actual game will take less time to calculate, and there is no The library of the unity engine is used so it can be completed in multi-threading.

Guess you like

Origin blog.csdn.net/tangyin025/article/details/123301572