Ascension has multiple procedural dungeon generation methods, but I do have a personal favorite… generating with prefabs! I’ll be talking about our main generator here, how it works, and some of the bumps along the way.
First things first
Why a prefab generator? What is a prefab generator? Typically, basic level generation is either done with a BSP tree, or by drawing random rooms and connecting them with hallways. These can provide quick, acceptable looking and playable levels, but can get boring quickly.
By using hand crafted rooms, randomly placed, you can create more interesting levels with relative ease. Instead of a bunch of square rooms, you can use any shape or design you want.
Now that we’ve got the basic idea out of the way, lets get cooking!
First off, we need a pool of hand crafted rooms. This is one area where more is better. The more prefab rooms you have, the better results you’ll get. Currently, we have around 20 rooms, which is on the very low end for decent results.
Next, we have to figure out how we’re going to connect these rooms. For Ascension, all of our prefabs have doorways built into the rooms. Since we want as much control over hallway placement as we can get, we force hallways to start where we want them at all times. As a result, we can dictate where hallways start, and end, at all times.
Finally, we need a way to draw these connections intelligently. The simplest solution for this situation is to use path finding. Since we know the starting and ending points, this feels like the most natural approach. Now, onto the building!
The Meat and Potatoes
The first thing we do, is create a map full of walls, and plop a randomly chosen room in the center of the map. At the same time, we create an inverted path finding map, as well as extending the unwalkable border out a single tile outside of the room. Next, we randomly choose a place for the second room and set the new room and all immediately adjacent tiles to un walkable.
We invert the walkable map so that we do not draw hallways through rooms and we extend room borders so that we always have a defined room. This allows pathing to walk around rooms and creates more interesting hallways.
With the second room placed, we randomly pick from the predefined doors from both rooms and set both places to be walkable. Then we path from door to door.
After these two rooms are placed, we start working with the tweakables of the generator and set some hard rules in place. The three unbreakable hard rules for this generator are:
- Rooms cannot be plopped over already existing rooms, with a tweakable to allow them to generate over hallways.
- Hallways must always terminate if they reach an already walkable tile which should always be a hallway, since we should already be pathing around rooms.
- All rooms must be connected to the first generated room, either connected directly, or through another room or hallway
With these rules in place, what tweakables are there? There are quite a few! We have the number of rooms to generate, the number of attempts to place a room, secondary room connection chance, maximum distance before we force a secondary connection, and the chance to create a secondary connection to the closest neighboring room. The last three I’ll touch on in a moment.
Get on with it!
Depending on what tweakables we have set, we try to find a place to plop the next rooms, until we either run out of tries, or we have reached the requested number of rooms. The most significant tweakable is plopping rooms over hallways. Turning this off leads to levels with longer hallways and fewer rooms, if rooms generate far enough away from each other.
The next important aspect is secondary connections. These are exactly what they sound like, they are a second connection to a second room. This is an easy way to create small loops to make exploring easier. We decide these in a few different ways. First, there is a random chance for any room to create a second connection to any other room. There is a tweakable to force this second connection to be to the closest room, which comes in handy for the next way to generate another connection.
The long trek
The biggest problem I had with this generator, was handling extremely long hallways. Not only are they boring, but they are bad for gameplay. I mitigated this issue to an extent with hallways merging together, but it didn’t always solve the problem.
The final tweakable consists of how long a hallway can be, before we force a secondary connection to the next closest room. This helps create short cuts, and helps solve the issue with the long hallways. They are still there, but they are not the only route to get to the room.
Sometimes, we will get generated a room that has no viable connection to the rest of the map. If the room fails the connectivity check, we simply walk back the hallway, and delete the room, and fail the attempt. Simple and easy way to ensure connectivity between the whole level. This can save a little bit of time doing it at generation, rather than waiting until the level is finished and checking every tile.
During each room generation, we are also placing things like static lights, monster spawners, doors, traps, chests, and anything else that may be in the room.
Finally, after the entire dungeon is generated, we go back through the room, and place items on the floor at random. I don’t allow everything to be generated on the floor, since I will be controlling certain drops dynamically based on how well the player is doing.
And that’s it, we now have a completed map!
Improvements and additions
Adding in prefabricated hallways is the next improvement. This would allow more precise control over room placement and would allow varying sized hallways. Another improvement would be attempting to fill in more space in the level with rooms. We can hide long corridor dead end rooms behind secret doors. We can also add in scripted rooms to create puzzles or rewards behind curated challenges.