CAGD 377 - Blog Post 4
- Els Fouche
- Oct 23, 2025
- 6 min read
Plague Break
Plague Break is a match 3 game with combat and roguelite elements where you play as a survivor struggling against the zombie hoard. Creating matches allows you to damage enemies; special tiles can be mixed into a match to do bonus damage or to heal yourself. Managing your board state carefully is critical to your survival.
Save System
The features we wanted to add during this sprint required a method of reading and writing persistent data to the file system. The common, naive approach to data persistence across scenes in Unity is to create some sort of data manager that is marked to not be destroyed when loading a scene. This data manager then holds whatever information needs to be accessed by other objects in a scene and simultaneously this approach allows for a very simplistic approach to scene loading.
The issue with the persistent data manager approach is that player progress is only persistent with respect to that instance of the application. If the player closes the game they will lose all progress. This was not the behavior our designer wanted and so I implemented a save/load system that uses Unity's json utility to serialize and deserialize game data. The (de)serialized data is read/written to file using C#'s StreamReader and StreamWriter classes, respectively. Data is stored at the platform-specified persistent data storage path. The system allows for multiple profile names, as well, should the designer want to account for additional users.
The above video shows one of the features that required persistent data. The designer requested that players should not be able to re-select levels from the level selection screen that they've already completed. Though it isn't shown directly in the video, exiting and re-entering play mode (e.g. closing and re-opening the game) does not reset the levels completion status.
The default behavior in Unity for a basic scene load is to destroy everything (except the objects marked to not be, as stated above) prior to loading the new scene. This prevents the engine from generating multiples of objects that require having only a single instance of that object, such as an audio listener or the event system. This simplistic approach is inflexible in some ways; if a designer wants the player to smoothly transition between levels or sections of levels it is not performant to load everything all at once. Additive loading solves this issue.
Additive Level Loading
Unity's Scene Manager allows for scenes to be loaded 'additively'. Loading a scene in this way creates a partitioned section of the object hierarchy that holds all of the objects that exist in the scene to be loaded. This is highly error prone if not set up correctly due to the above-mentioned issues with objects that require only a single instance be loaded at a time.
However, also as mentioned above, it is a very powerful system that allows for levels to be divided up into sections that are loaded or unloaded based on e.g. the player's position in the level. To do so it's recommended that a scene handler be set up that stores a reference to the scenes loaded to avoid unnecessary queries to Unity's Scene Manager and string comparisons. Additionally, it's highly recommended that scenes that are designed to be loaded additively be differentiated from standard scenes in some way. This is because scenes that are meant to be loaded additively often do not have necessary components to function, such as a main camera and other 'single instance only' objects as discussed previously.
Note the hierarchy on the left side of the video above. I've utilized a 'preload scene' that contains objects that are expected to persist across all scenes of the game such as the main camera, data handlers, etc. This preload scene is loaded on game start. When it's initialized, it checks to see if any scenes are currently loaded (besides itself) and if none are it loads the main menu. It then loads and unloads scenes as needed.
Shopping!
A key part of the designer's vision for the project is a permanent upgrade shop that allows the player to spend 'crystals' obtained during play to modify their experience e.g. with health upgrades. I'm not certain why zombies drop crystals but as of this sprint they do. Players earn crystals by beating enemy waves and may return to the shop to spend them on improvements such as increasing their health or damage.
The crystals and the improvements are all tracked in the save data, allowing the player to earn them at their own pace. Future improvements to the system include additional upgrades as well as infinitely available upgrades that gradually increase in price.
Issues
Old Issues
Issue #03: The game resolution did not adapt properly to common phone aspect ratios.
This issue was marked as low priority in sprint 3. It was corrected during this sprint.
Action:
The video shows the aspect ratio fix that I implemented. The primary issue we were having with regards to the game's appearance on varied devices was that the game board was not responsive to the device's aspect ratio. I corrected this by modifying the game board's size based on the main camera's orthogonal size.
Issue #04: The reliance on colors for matching made it unnecessarily difficult for one color-blind playtester.
This issue was marked as low priority in sprint 3. It was corrected during this sprint.
Action:
This video showcases the implementation of the final game pieces. This was only a tiny bit more complex than simply plugging in the correct pieces due to the way the system was initially set up. When a piece is generated there's a switch block that originally and simply set the material color directly. It was a trivial fix to add in a material selection from an array instead.
The system is, however, not robust at all. The switch block must be manually updated if the designer would like any new piece types to be added. I don't foresee this being an issue due to the limited scope of this project.
Issue #05: The minimum API level was not set appropriately, resulting in some testers being unable to play the game.
Action: This issue has been addressed as of alpha build 0.1.0. The game now supports Android version 13 and up.
New Issues
Issue #06: On advancing a wave the game board did not always generate non-matching pieces.
Action: The board reset method was converted to an asynchronous method with lockouts to prevent multiple calls to the method and to prevent the player from sending any input during the reset.
The reset method attempts to randomly set a piece to one of the possible piece types. It then checks for matches. If a match of 3 or more is found in any direction, it removes that piece from the list of possible pieces and re-checks with a new, randomly selected piece. It makes 10 attempts before it gives up and moves on to the next piece to prevent an infinite loop.
The above means that, except in extremely rare situations, all generated pieces will not have more than one matching adjacent piece in any of the four directions.
Issue #07: The player is unable to pause the game.
Action: This was an issue caused by an auto-wiring problem with the player controller. Specifically, the player controller is loaded only for levels that require it. When it's loaded it seeks out the main camera, the event system, etc.
Due to asynchronous nature of the additive loading system, the player controller was grabbing a reference to the wrong objects which meant UI elements, such as the pause button, were not working in battle levels.
Issue #08: One of the levels on the level select screen was non-functional.
Action: Simple fix, the level button did not have a valid level set.
Issue #09: Players could double-tap a level-loading button to force two instances of that level to load simultaneously, resulting in a fatal error.
Action: This required that I instituted lockout protocols and multi-click prevention strategies in various scripts. These systems are redundant and complimentary. I designed them such that if a new script fails to implement the correct lockout procedure, existing scripts its communicating with should catch the error and prevent it.
Issue #10: When the player dies enemies remain on screen.
Action: Due to the additive level loading enemies were not being unloaded when the player died. This was due to enemies being spawned in as part of the root scene rather than as part of the battle scene, allowing them to ignore the unload directive from the scene handler.
To correct this I modified the enemies such that they remain part of their scene when spawned. I also added an additional cleanup check to the scene handler to unload all objects that are not part of either the preload scene or the next scene to be loaded.

Comments