I made great progress on improving map generation this week. I almost threw out the structure-first technique I’ve been using to generate levels in favor of a story-first approach. Basically, I wanted to create a procedurally generated backstory for each level and generate the structure and contents from that. That was way too difficult. Maybe in the next roguelike… Also, I wasn’t fully appreciating the advantages of structure-first generation, like efficient use of space.
- Restored visual map generation and room graph generation. These features stopped working after the last major refactoring because I modified the startup process. Now that my attention is back on map generation, I needed to get them working again. It has harder to get these working again than I anticipated because the startup logic is complicated. The game is initiated by events in the Title, Class Select, and Game scene. Multiple Unity GameObjects perform initialization in the Game scene. There are multiple parameters that drive different paths through the initialization – whether visual map generation is on, where a screenshot of the entire map is captured after generation completes, whether a map is being generated or loaded. I didn’t make real progress until I put the main methods and events down on paper. The logic filled the entire sheet. Being able to see all of it at a glance made the required fixes, and simplifications, obvious. The main reason I needed to do this was that the map image was disappearing before the screen capture completed. Unity’s main screenshot method doesn’t immediately capture the screenshot. I ended up using the CaptureScreenshotAsTexture method instead and moving the code to an earlier point in the initialization.
- Started on Map Elements. “Map Element” is the term I’m using to describe the elements that the map is populated with after the structure is generated. These can be simple objects and enemies, events, room decorators, puzzles, etc. Each Map Element has its own mini-PCG for variation, and constraints defining where it can be placed. The first Map Element I created was “Challenge Reward.” This Map Element finds a two-room sequence and places a difficult enemy in one room and an item in the adjacent room.
- Mandatory room identification. The level generation can now determine which rooms must be traversed to complete the level, regardless of the path taken. This is useful for placing dependent Map Elements, such as a locked door and key. To identify the mandatory rooms, first a depth-first search is done on the room graph to construct all possible paths from the starting room to the ending room. Then, the rooms that exist in each path are identified. In the end, the solution was straightforward, but I spent a lot of time getting to that solution.
- Improved room graphs. Room graphs now show mandatory rooms and linear sequences of rooms starting or ending with a dead-end (good candidates for placing Map Elements).
Next week, I’m going to build some more Map Elements and inject more randomization into level structure (varying room counts, sizes, etc.).