As I previously discussed, my current dungeon layout generation logic is hard-coded. However, I really need to iterate on my dungeon layouts and design. In fact, I need to see dozens if not hundreds of examples before I can say any of it is working. And I cannot keep changing the code every time. What I have is proof of concept and I could go two ways — keep writing code and hard-code all the logic or have it modular following external steps. I’m going with the second (and I really hope it won’t take too much overhead time).
First of all I want a dungeon layout definition asset that I can modify quickly in the editor and swap out as needed. This is also something I need in order to have different layouts based on level themes or other requirements.
In fact, this being an asset, I can modify it at runtime and regenerate the dungeon and it will use the new parameters.
The first part is to convert everything I have into modular “steps”, so that I can specify them one at a time. To begin with, I can simply convert the three branching instructions I have (main path, bunch of secondaries, bunch of tiny tertiaries) into self-contained methods and then have the layout specify that those steps/methods should be taken:
This results in the exactly the same dungeon as before (and this should remain the case until I’ve converted it all):
Of course, this lacks any sort of parameters. In fact, it’s only the step order that I’ve potentially changed here. Everything else is hard-coded. This is where the design question comes in: how much do I actually want to customize? The more I have to customize, the more finicky it gets. For example, I definitely want to control branch lengths:
I also want to be able to add and remove steps, and that requires me to be able to specify the origin(s) of the step I am doing. In this case, where the branches spawn from (and how many):
And similarly, I need to specify where the branches end (conclude), that is, what sort of rooms they generate at the end of branches so I can use those rooms for later steps:
And with that I can add the start and goal rooms dynamically as well using the origin logic:
A final tweak is to specify ranges for random selection rather than fixed values (in fact, I’m using an encapsulated data structure for this so I can technically have not just “random pick” but some sort of extra rules):
And at this point I’ve parametrized everything there is to parametrize that I previously anticipated I would want to change. From now on, it’s new features or modification of existing ones.
One caveat is that the dungeon can “fail” to generate some paths/branches when its current step cannot complete, namely, the branches can truncate if there isn’t enough space to place them:
The problem here is when I expect the branch to end with a certain conclusion, such a specific hub room (which, practically speaking, might be important boss or loot rooms). And if I end up with all branches broken, then the rest of the dungeon will not even generate. So there are two options here: either let the branch re-generate and try again or make it know how to truncate early. In fact, I likely need both — first retry, then truncate.
I will make retry logic for now. For that, I need to keep track of rooms I place during branch generation. And if it fails, I need to rollback all the changes I made. Undo/retry is generally a bit tricky, but thankfully, I am generating the first dungeon part with pure logic nodes, so it’s fairly easy to add/remove and connect/disconnect them.
Erasing a failed attempt just means keeping a list of the nodes that the current branch has already created and then removing and disconnecting them in the main map and list. I can’t really show this in a screenshot (since the layout just looks fine), but basically it removes the failed branches:
This works fine, so now I can add a certain number of iterations to branch placing. For this, I just made a certain total number of branches that I want from any of the origin points, and then I keep iterating until I have enough or run out of retries:
The algorithm, being almost purely random, can occasionally create a branch that blocks a lot of other branches that might want to get placed. So I end up with less rooms than I want. This isn’t ideal for balancing purposes. It’s something I will have to avoid with careful generation parameters and with adding better branches. Nevertheless, with some parameter tweakage, I can make even this simple naive branching logic work out decent most of the time:
There are still many improvements to make, big and small. But the big modularity part of it is done. From here I can make different dungeon layouts through the use of assets for different themes, difficulties, etc. I will cover further individual improvements and additions in different posts. I’ll have to do a whole another pass on this later.