Random Dungeon Generation


Introduction

Hello! I am Erick Latshaw and I am the Technical Director for Precipice Games' Whispering Abyss. This post should serve as an introduction to our project following the pre-production phases. Whispering Abyss is a projectile-based third-person dungeon rogue-lite.  In game development, things are very likely to change along the way and work might get scrapped. Luckily, this does not happen as often to engineers as it does for artists or designers. Considering this, we wanted our first tasks to be impactful mechanics with a foundation that represents our current game design that also allows for the flexibility of changing that game design in the coming months.

An incredibly important part of a rogue-lite is that the levels are procedurally generated. We are going with a classic dungeon-like level design where the level is broken up into rooms that are placed in a random orientation and location, as well as random objects within each room to create an ever-changing game that won't get visually stale too quickly. In preparations for a proof of concept for level generation, I wanted to stay in scope and decided that all the rooms would need four doorways, one in each cardinal direction, the distance between those doorways would have to be the same across rooms, and that all the doorways from each room variation would have to line up. To test my future implementation, I made 4 identical rooms with 4 "door" objects that when enabled, blended in with the wall, and when disabled, made an actual doorway. Each room was a different color, and the spawn room was yellow, which would only spawn once.


First Implementation

The very first thing I wanted to do was to spawn x number of rooms that all connected to each other. The spawn room would spawn first at the origin. This was rather easy. I had a list of room prefabs, with the spawn room being first, in order to allow random initialization of the rooms, and then kept track of the "coordinates" in a list (the coordinates represented a sort of type-based spawning). I then populated a second list of possible spawn locations for the next room. The possible spawn locations would of course be locations that are adjacent to a current room so that they would all connect no matter what. One of these locations would be picked, a random room would be initialized, that location would be added to the first list of current rooms, removed from the second list, and then the adjacent rooms for that newly spawned room would be calculated and added to the second list, taking into account where all the current rooms are. This continues until we have all x number of rooms.


Second Implementation

Now, this was great and was very easily achieved, but the rooms don't actually connect yet. I had initially thought that all rooms would connect to all adjacent rooms, but I quickly realized that this would be boring. Not all rooms should connect to each other. There should be some sort of maze structure in place. The problem with traditional maze algorithms is that they create "perfect" mazes, with much more structured solutions. We want imperfect mazes to create rooms that may all connect to each other and make larger rooms, as well as multiple paths to a given destination. Remember that this is a game where we have to fight enemies through a random level. It would be very difficult if there was only one way to the end and would be much better if there were intertwining paths as well as multiple exciting rooms along the way. I implemented an imperfect maze generation but as I envisioned, it was a little too imperfect. Rooms would get entirely cut off from the rest of the level, and sometimes as much as half the level would be inaccessible. This was unfortunate but not a total waste of time. Here's what it might have looked like:

This was a fairly good generation, and the only room that was cut off completely was the blue room towards the top. It represents what I was going for though.


Final Implementation

I decided to write some logic that would essentially path find from every room to the spawn room. To keep performance in mind, I sorted the rooms by their distance from the spawn room. Keep in mind there is a lot more to the code now than what I had mentioned in the first part, and the rooms are organized in a different way and don't get initialized until the very end. Each room would randomly "path find" to spawn. Essentially going to any adjacent room until it found spawn. All these rooms then became "visited" and any future room only had to path find to a "visited" room. This solution worked out well and seemed to be decently performant, even allowing me to have 2000 rooms spawn at once and all connect in random ways.

(This is not 2000 rooms, sorry).

Get Whispering Abyss (2022)

Leave a comment

Log in with itch.io to leave a comment.