This procedural map generation idea is not solely my own. (Please refer to the link for more details.)
I referenced a reddit post and made some modifications in an effort to create a more flexible map generator. There may be inefficient or incorrect parts, so if you notice anything, please let me know via email, and I will do my best to fix it ASAP.
- Flexibility : with lots of parameters
- Toggle Visualization : apply it directly to your project
- Everything is modularized : easily customizable.
Unity Version | Compatibility |
---|---|
2022.3.47f | ✔️ |
2022.3.48f | ✔️ |
If only C# scripts are used, then other versions are also sufficiently usable.
This is visualized process of Procedural Map Generation.
It can be used for Loguelike
, DungeonCrawl
or other games if you need random map in runtime.
You can use it by starting coroutine named MapGeneratedCoroutine
in MapGenerator.cs
// If you want to call in other script,
// apply Singleton on @MapGenerator in scene
StartCoroutine(MapGenerateCoroutine());
There are lots of parameters and comments are also there. But I thought some of the comments would be insufficient for the users.
( Cuz I'm not very good at English... )
So, I will provide additional explanations about the parameters here.
RandomSpawnType
means shape of spawnable region. There are two shapes. (Oval and Rectangle)
Below that, SpawnRegionSize
represents the width and height of that type.
However, this does not guarantee that the map will be created in that shape.
Since spawnign locations, physics simulations, room selections... Everything is random, no one can predict what shape the map will take.
SelectRoomCnt
refers to the number of rooms to be selected, and it generates rooms with sizes between minRoomSize
and maxRoomSize
based on this count.
GenerateRoomCnt
is the total count of rooms generated at the beginning. Naturally, SelectRoomCnt
is included in this.
And the remaining rooms, excluding SelectRoom
, help create more diverse maps by increasing the distance between SelectRoom
.
If the overlapping area is wide, a straight hallway can be installed between the rooms; however, if the overlapping area is narrow, the hallways may protrude awkwardly from both sides of the rooms.
Therefore, OverlapWidth
is a parameter that defines the criteria for this.
This parameter doesn't work now.
I'll update later.
I implemented smoothing using cellular automata. The SmoothLevel
serves as a parameter for the cellular automata; as the value decreases, the result becomes smoother, while as the value increases, it becomes more angular.
Recommendation : You can set values from 1 to 8, but if you are using smoothing, I recommend values of 4 or higher.
As mentioned earlier, this is not entirely my idea. It has been modified to suit my needs. If you would like to check the original idea, please refer to the link.
This desrciption is intended to assist those who wish to customize this code or make it more efficient.
First, we should spawn rooms in region set before.
You need to create enough rooms to increase the distance between the Main Rooms, and some of these will also be used as hallways.
Then, you need to select a Main Room that meets the criteria. I chose a room that is large enough and has a balanced proportion without being skewed to one side
for (int i = 0; i < rooms.Count; i++)
{
// Other codes...
float size = scale.x * scale.y; // Calculate size of room
float ratio = scale.x / scale.y; //
if (ratio > 2f || ratio < 0.5f) continue; // Ignore unbalance rooms
tmpRooms.Add((size, i));
}
// Then sort tmpRooms, select rooms from 0-index
After physics simulation, we must rasterize data into 2D Array.
This process is important but not difficult, so you will likely understand it after just looking at the code once.
Please refer to the GenerateMapArr
and MainRoomFraming
function in MapGenerator.cs
To fully understand this process, prior knowledge of Delaunay triangulation and Minimum Spanning Tree (MST) is required.
After connecting all the rooms through Delaunay triangulation, we need to create the MST to simplify the edges.
MST is a tree structure that includes all vertices in a given graph while minimizing the total weight of the edges.
We can find the MST using Kruskal's algoritm. and it isn't complex.
The issue is Delaunay triangulation.
I used the Bowyer-Watson algorithm to compute the Delaunay triangulation, and watching a YouTube video will help in understanding it.
The Bowyer-Watson algorithm features a SuperTriangle
, which represents an infinitely large triangle.
As a result, in the actual game, there may be cases where not all rooms are connected, requiring either increasing the size of the SuperTriangle or implementing separate exception handling.
All the rooms have been connected through the above process. Now, we need to appropriately generate corridors to connect the rooms.
This process is influenced by the previously mentioned parameter, Overlap Width
, where a larger overlapping area results in straight corridors, and a smaller overlapping area leads to L-shaped corridors.
In particular, if the L-shaped corridors are created randomly, a rectangular-shaped map may be generated. To prevent this, the corners of the 'L' are designed to face the center of the map.
// Get center of vertices
int mapCenterX = map.GetLength(0) / 2;
int mapCenterY = map.GetLength(1) / 2;
int midX = (start.x + end.x) / 2;
int midY = (start.y + end.y) / 2;
int quadrant = DetermineQuadrant(midX - mapCenterX - minX, midY - mapCenterY - minY);
// Determine hallway ('L' or flipped 'L')
if (quadrant == 2 || quadrant == 3)
{
CreateStraightHallway(start.x, start.y,end.x, start.y); // Generate horizontal hallway first
CreateStraightHallway(end.x, start.y, end.x, end.y); // Then vertical hallway
}
else if (quadrant == 1 || quadrant == 4)
{
CreateStraightHallway(start.x, start.y, start.x, end.y); // Generate ertical hallway first
CreateStraightHallway(start.x, end.y, end.x, end.y); // Then horizontal hallway
}
While we can obtain map information through the procedural map generator, it cannot be used directly in the game.
Instead, we will use that information to perform AutoTiling and create a map that can actually be used in the game.
The assets used are as follows: Unity Asset Store
One characteristic of this asset is that the walls are made up of two grids.
Considering this feature, I implemented a system using Layer Masks to lay down the appropriate tiles.
There are lots of parameters, but simply two things : Tilemap and Tile
I seperate tilemap because of layer order, but it isn't perfect. ( Only 1x1 Grid Chrarcter can move free )
If you have any suggestions for improvements, please send me an email.
The tiles below determine which tiles to place based on certain rules.
All the rules are written in Bitmask, and I have not yet noticed any awkward parts that stand out. Like this: