Weekly Update – October 16, 2021

I rate the progress I made this week a 3 out of 10 due to available time. I’ve been trying to work on the game as soon as I get home from work because I sometimes have a free hour, but my mental energy tank is proving to be empty at the time of day. The weekday schedule that’s working best for me, in terms of mental sharpness, is early morning (5:30 AM) and later in the evening (8 or 9 PM). I also try to do some design work during lunch so that I know exactly what I’m working on next when it’s time to sit down and code.

Anyway, I continued refining map generation this week. There were a few noteworthy accomplishments:

  • Map configuration randomization. This adds another layer of randomization to level generation. It randomizes map configuration parameters such as number and size of rooms, room spacing, and minimum and maximum corridor widths. It has the effect of changing the overall feel of a map and how it’s played.
  • Entity collection types. These allow entities to belong to one or more collections in a data-driven manner. These collections are then used during procedural generation to randomly select an entity from a list of applicable entities. For example, a storage room may contain barrels, crates, and/or sacks. 
  • Implemented new map elements: fountain, fountain area, abandoned area. The fountain map element places a fountain on the map. The fountain area is responsible for finding an area on the map, such as an unoccupied room, to place the fountain. The abandoned area map element adds debris and cobwebs to an area that has been designated as long abandoned. As with all map elements in the game, these elements are parameterized and support random or specific values. For example, a fountain can heal the player, poison the player, or be dry. This can be randomized by the fountain map element, or the fountain area map element can pass a specific value. Map elements can be hierarchical and layered. For instance, the fountain map element is a child of the fountain area map element. The abandoned area map element can optionally be layered on other map elements, such as the fountain area, to make some areas of a map appear abandoned.

Next week, I’ll convert some existing map features, such as hidden and boarded up doors, to map elements. This will enable these features to be placed more effectively. For example, a player may not be able to break down a boarded up door, so those shouldn’t be placed on the critical path through the level. I also want to get a few new map elements in.

Looking forward to Roguelike Celebration 2021!

Weekly Update – October 8, 2021

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).
Room graph

Next week, I’m going to build some more Map Elements and inject more randomization into level structure (varying room counts, sizes, etc.).

Weekly Update – October 1, 2021

My new mantra is “always maintain a playable game.” At any given time, the key features should all be working and playing the game should be a complete, coherent experience. I have a habit of breaking systems and putting off fixing them while I focus on specific aspects of the game. I’m adjusting my approach so that I maintain the core experience and expand out from it in bite-sized chunks. The intent is to get to a public release sooner and iterate faster on gameplay refinement.

This Week’s Achievements

  • Restored moving back and forth between levels. This got broken months ago when I overhauled how actors interact with objects. This was fairly straightforward to get working again. I just had to add a new action type for when a player clicks on a stairs object.
  • Music manager. To support music and sound effects needing to play across a scene transition, I added a music manager object. It’s basically just an object with DontDestroyOnLoad enabled to preserve it across scenes.
  • Hotbar improvements. Starting usable items and usable items that are picked up are automatically added to the hotbar.
  • More sound effects. Healing, ghosts appearing, walking on rubble, starting a new game.
  • Added tooltips for game buttons. Hovering over the main game buttons now shows a tooltip explaining the button’s function.
  • Miscellaneous visual effects. Items now spin when thrown. Poisonous gas is now translucent.
  • More bug fixes. On average, bug fixing continues to take less time after the last code redesign/refactoring. I haven’t found anything that needs to be reworked in a long time.
  • Rethinking level generation. The BSP algorithm I’m using to create and place rooms doesn’t work well with how I want to populate levels. I spent many hours this week researching and thinking about different approaches. 

Next Week’s Goals

Next week, I’ll spend more time rethinking level generation and hopefully starting coding on a new approach.

Weekly Update – July 2, 2021

Dev time was extremely limited this week. After reprioritizing Release 3 features, my plan for the week changed and I ended up working on the save system. This was functioning in Release 1 but I haven’t updated it for all the features and refactoring done in Release 2. So much has changed that I’m basically starting over. I’m also having to rework some classes that contain a mix of data that should be saved and data that shouldn’t or can’t be saved (e.g., MonoBehaviours). I read a lot of articles and watched a lot of videos on save systems in Unity. Most of what I found was aimed at beginners and only covered the basics. This video was helpful, but I didn’t like having to create a new temporary object and save and load methods with explicit setters and getters; it’s slow performance-wise and difficult to maintain. I’d rather just isolate the data that needs to be saved and serialize it. Either I haven’t discovered the right pattern, or saving complex data in Unity requires a lot of effort and there’s no way around it. I could put all of an object’s state data into a single plain C# class, but then I couldn’t use composition to build game objects. I could isolate the state data on each game object component, but then I’d have to individually serialize the data in each component, or aggregate the stage data spread across the components first. Ideally, the save system would simply save and restore game objects marked for saving and restoring. This is technically possible in Unity, but from what I’ve gathered it’s not advisable. Anyway, after some analysis paralysis, I started implementing the new save system starting with the map. Once a monolithic class, the map now simply contains its dimensions, a cell collection, and half a dozen other collections that are only used for procedural generation, such as the room collection. I don’t believe I need to save the procedural generation data after the map is created, so I’m not going to worry about saving that data for now (ideally, this data would be in a class specifically for map generation that is destroyed after the map is created; will refactor in the future). So, the bulk of the data being saved is the cell collection. Each cell contains multiple attributes, including value and reference types. By default, the serializer I’m using (Json.NET) serializes objects by value. This is a problem for “type” objects. For example, each cell has a cell type, such as a wall or a floor. Each cell type has many attributes. By default, Json.NET serializes all of the attributes in the cell type for each cell that has that cell type. This increases the size of the save file significantly and loses the reference to the original cell type object. I solved this problem by adding an attribute for the cell type’s unique id to the cell class. The advantages of this solution are 1) less data written to file and 2) when loading data, the reference to the cell type object can be restored by retrieving the cell type from a dictionary using the unique id as the key. I applied this pattern to other attributes as well, such as actor types and item types. As of now, the map successfully saves to file, but loading hasn’t been implemented.

Other than that, I briefly descended into a rabbit hole on story-driven procedural map generation. This came out of the map generation work I did last week on room types. I want maps to have some coherence or purpose rather than just being a random assortment of rooms, and cohesion across levels that is driven by an overarching procedurally generated story. Ideally, the player’s actions could alter the story and influence future levels. I created a conceptual framework for doing this and implemented some of the building blocks. However, after reprioritizing Release 3 features, this is on the backburner for now.

Next week, the goal is to get saving and loading working again and start on the hotbar.

Weekly Update – January 16, 2021

This week I completed the map generation visualizer. I made a short video to demonstrate. This is best viewed frame-by-frame if you want to get into the details of each step. I’ll create a longer video with commentary at some point.

I fixed a bunch of bugs in the map generator and map generation visualizer. It’s amazing how much faster I’ve been able to spot and fix bugs after overhauling the map generator and being able to see the exact step at which an issue occurs in the visualizer.

Next week, I’m going to add some new map content – new room types, new interactive objects, improved locked door/key placement.

Weekly Update – January 10, 2021

Work on the new map generator is winding down, finally! I recently added a feature to visualize the graph structure of the generated maps. This has been a huge help in analyzing the map flows and determining where to place various elements. Each time a new map is generated, an image file of the graph is created using Graphviz () and a screenshot of the map is taken. Here’s an example of a generated map and the associated graph:

Graph visualization example
Map graph visualization example

Another feature of the new map generator is the ability to watch the map generate step-by-step. This feature has helped immensely in finding map generation bugs and improvements. I’ll put together a video for the next Sharing Saturday.

Next week, with the recently developed map visualization features, I’ll be fine-tuning the map generation parameters and experimenting with lock and key placement.