ProcJam 25 Diary - p1 dungeon gen

Procjam

Its ProcJam time again https://itch.io/jam/procjam and this year I decided that rather than spin up a new side-project that will end up unfinished in a dusty archive .. (see here) I would revamp something that I had worked on previously but never really made presentable.

So this is the first post in what I hope to be a short diary series about my benched spooky-liminal-horror SCP building generator. 

The final version is a randomly generated ruined bunker style lab, inspired by Stalker, SCP and the backrooms. I will explain a bunch of the systems that enabled the final 3D version in a later post. But for now I'm going to talk about the basis for this project, and many other PCG games. The dungeon generator! Its pictured in the header image here and you can play with the WebGL version over on itch at this link

https://tomnullpointer.itch.io/multi-floor-dungeon-generator

Although I am a very visual designer I still try to build the environmental structure of my games into an abstracted data form. This is partly because I'm lazy and I hate laying out endless props and wall pieces, but also because having your game world in an independent data structure makes it much easier to change elements of the world in a direct and manageable way. It also means that when you come to generating the 3d visuals you can skin the data in a variety of different ways without needing to change its data model counterpart.

To create the bunker I wanted to work with some form of dungeon room/corridor map. And as with another old project of mine In Ruins I wanted to sort of invert the construction, turning corridors into causeways and solid earth into air. I thought this would help to produce the more external industrial architecture you might find at an abandoned base. I realised that I could probably achieve this with a few tweaks to a standard dungeon generator system.

Starting with Donjon d20

I started by replicating most of the functions used in the Donjon d20 generator. This is a favourite of mine, partly because of its flexibility, but also because it tends to generate fairly compact spaces with a nice mix of room sized areas and mazelike corridors.

The original code is available on the site, so I ported most of the system into c# for unity. The basic generation algorithm is as follows

1. Define an empty map of x by y tiles

2. Try to place a room of a randomly chosen dimension (between min and max dimension) somewhere in the map

3. If its possible to place the room, add it and mark all the tiles that room encompasses with an id for the room. 

4. Repeat from step 2 until you have enough rooms (or close enough)

5. Loop over all the placed rooms and mark the edge cells as walls, also mark some of those edge cells as potential exits.

6. Start to fill the map with corridors. (This part gets its own subsection)

  • Start a corridor in an empty tile.
  • Dig out the corridor in a random direction and change direction if is blocked, or based on other stylistic factors (i.e. always change direction ever 4 cells to make a very windy tunnel)
  • If a tunnel can connect to an adjacent door way then connect it!
  • If a corridor cannot proceed any further in any direction then move to the next available empty tile and start a new corridor again from the first step.
  • Once enough corridors have been placed or enough doorways have been linked exit this stage. (again this can be a stylistic choice)

Pruning

The approach described above will generally result in a lot of corridors! In many cases the entire map will end up packed full of rooms and corridors. This will connect most together, but can end up with a lot of useless extra corridors or dead ends. So now its time to prune!

Any room that has a door-exit which doesn't link to a corridor has that tile replaced with a wall tile. Then a recursive process checks every corridor tile. if the tile has only 1 other corridor as a neighbour it is a dead-end and the tile is removed. This process of removing dead-end corridor tiles is repeated, causing corridors to shrink back each pass until all the dead ends are removed. You can obviously leave some if you like the way they look!

An Odd Trick

The secret sauce of this entire approach is its adherence to what id call an odd/even placement rule. If we were to place rooms and corridors entirely randomly we would end up with corridors and rooms running parallel to each other in adjacent tiles. This would make some corridors feel like they were 2 tiles wide and causes all kinds of artefacts melding rooms with corridors etc.

So instead we ONLY use rooms of odd dimensions and we ONLY place doors and corridor spawns on odd tiles. As we dig corridors each movement step also needs to be of a length that will leave the current corridor at another odd grid co-ordinate. If we stick to this rule then all corridors are guaranteed to run with a single tile border of solid dungeon around them and doors will always be aligned to these corridor paths. I've added a few col/row numbers and a pink highlight below to show how this works.

Multi-floor

Now I've used almost this exact algorithm before to generate dungeons or ruins. But I was interested to see how it might be extended into 3d. What if the dungeon rooms were able to exist over multiple floors? We might be able to have larger spaces with high ceilings that could allow players to enter a room on one floor but leave on another.

Making this addition turned out to be fairly trivial. Instead of a 2d array of tiles I made a 3d array, where each y layer is a separate floor of the dungeon. When generating the floor 0 I allow any successfully placed room to to have a y dimension (in addition to its existing width and depth) to represent the number of floors it extends upwards to. 

Then each subsequent floor above won't generate new rooms until it has checked for existing rooms below that might "punch through" to its level. If a lower room does reach into the new layer it is placed just like a new one, but in the original location of its lower level source.

The image above shows floor 0 and floor 1 of a random generation. Here Room A exists on both floors and in the second layer has a balcony around its edge, a spiral staircase (to allow players to move up and down) and a new corridor connecting it to the east. The rooms B and C also extend in the same way, except that on the second floor C has only a balcony and no onward corridors.

Ok so we now have a system that will generate the basic room and corridor information I might use for building this bunker

The image above shows the sort of map I had in mind for the bunker/lab idea. If I were to imagine the corridors as raised causeways or gantries and each room as a separate holding area I could start to generate the sort of space I had in mind. The fact that some of the corridor connections might seem a little nonsensical is actually fine in the fiction of a liminal SCP space since logical connections are often replaced with more surreal architecture.

Next time.. from 2d to 3d

This article was updated on