(Originally published on my blog on December 19, 2021)
Over the summer, myself and a few friends hatched an idea to work on a project together to practice programming. A bit of brainstorming later and we settled on a game idea that is sort of a hybrid roguelike / Legend of Zelda style game, and started hashing out some details; finally we had enough of a working name to put together a Unity project and a github repo, and started banging out some code. (I am sure Project Dungeon will not be the final name of this project.... I hope....)
With twenty rooms:
The blue room is considered the "starting room" and the red room is considered the "boss room". This data at this stage isn't interactable in any way, but shows a proof of concept in the code to build the layout, correctly assign doors, and pathfind from a randomized starting room to the most distant dead end room in the dungeon, which we then assign as a boss room.
Originally this was implemented using
Random, but after watching a few Game Developer Conference talks on using "random" functions vs using a seed and hashing, I convinced my co-developers that we should use hashing to create noise, instead of random number generators, to determine our "random" generation. This way, every dungeon generated from the same seed will have the same layout, monster placement, and so on. This will allow different players to start in the same world if they choose, and also increase the reproducibility of behavior for testing and bug hunting.
Ajesh adapted the C++ function that Squirrel Eiserloh shows at 46:33 in his GDC talk, Math for Game Developers: Noise-Based RNG into C# for us, and swapped our implementation over to use the new function. It works great! There will be some interesting challenges to solve in the future using this (for one, if we have one world seed used to generate all hashes, how do we handle changing the dungeon layouts when a character dies and we want to generate new ones?), but I think overall it is the correct choice. Besides, it's fun to generate a layout, mess with the seed, generate a new layout, then switch back to the first one and see the first layout again!
I volunteered to handle the tilemap generation end of things, the actual generating of the rooms described by the layout generator as in-game objects. I figured I would be best positioned to do this as I'm also planning to handle the majority of the art assets as well.
Following parts of this video tutorial series on generating procedural dungeons in Unity by Sunny Valley Studio, and using this 32x32 dungeon tileset by Stealthix for prototyping, I got the code into a situation where I could paint tiles into a tilemap programmatically:
But then I basically ran into a wall. I was thinking I would have to generate each room as an object and create a prefab or something to pass back to an array of rooms for the game engine to pick from as needed, and I just didn't know enough about Unity. I had no idea how to do that. Honestly, I still don't. Most of my time on this project in the past couple months has been spent on other things, like setting up
SpriteLibraryAssets and animations, and trying to get a better handle on how the different parts of Unity work together and interact in general.
But... Saturday I had a different idea.
What if we use each floor of the dungeon as its own tilemap? This idea will probably come with optimization challenges, especially if we go for larger dungeons. But I can see a path to making this work! And any working prototype is better than none. Let's do this!
Next up in part 2: adapting the Room Generator code to take floor and wall tilemaps, room dimensions, and starting coordinates as arguments. Also work on assigning wall tiles manually, since trying to do it with a rule tile ... didn't turn out good, and probably even less so once I switch to using the assets I created that are 3 units deep for the complete height of a wall on-screen. Oh, yeah, and at some point I'll need to finish figuring out the doors and room transitions to be able to generate them with code. I'm sure it's doable.