Defeat grocery-themed bosses in this boss rush game submitted for the Boss Rush Jam 2024.
Defeat grocery-themed bosses in this boss rush game submitted for the Boss Rush Jam 2024.
Roles: Programmer, In-engine developer, AI Programmer, Tools Programmer  |  Timeline: 2024  |  1 month
Started as a Game Jam Submission for the Boss Rush Jam 2024 with the theme of "Exchange". Our interpretation of the theme was that the bosses throw projectiles at the player to which the player can pick up and send back at them. Exchanging projectiles.
A Custom Inspector tool to help designers create unique projectile attacks for our bosses.
There's 3 main goals I outlined when programming this tool:
Make a designer-friendly interface with no programming required
Use Scriptable Objects to store each projectile pattern
Ability to stack modifiers to create unique effects
The idea to make this tool started 1 week before our team had to showcase Trials of the Aisles at the CNE 2024. Up until this point I hard-coded the projectile attack patterns of each boss but they felt bland and predictable, two feeling we didn't want for our fast-paced bullet hell game.
There was no way we would be able to create many unique projectile patterns for our bosses if we hard-coded the outputs, so we thought of this tool as a solution. My teammate sent me this video series of making a projectile pattern tool using PICO-8 by Lazy Devs. My job was to implement this in Unity, a difficult challenge since I had to interpret all the code to C# and Unity specifically with Editor Scripting.
Why this design is good
Colour/Contrast - It's easy to tell which pattern we are using (blue highlighted background)
Modifier Grouping - It's easy to tell which patterns contain which modifiers since they're indented below the pattern. There's also a foldout so we can collapse the whole group to save space.
Constraints - The "Pattern Number to Use" field is constrained between 0 and the highest pattern number in the list and can't exceed any of those values which prevents accidental human error.
Reducing Human Error - Ability to Undo/Redo actions which fosters experimentation in user's interactions with the tool.
Why this design is bad:
High Cognitive Load - These are all float parameters, meaning the user has to think of the number and then input it manually in the field instead of acting on it through buttons. Since there's many fields, it could bloat the visual load of the inspector and thus we have to spend more time looking where we need to adjust the values.
Poor representation - Some fields are better off as other variable types like booleans or integers. The "Mirror" and "Shift" variables in the "Spread" pattern for example can only have values of 0 and 1, where 0 is false, and 1 is true. This should be represented through a boolean so the designers can intuitively know how to interact with it.
Pattern Grouping - There is no spacing between the Base Pattern and the extra patterns we add which could make it seem like the base pattern behaves like the others, but unlike the extra patterns we can't delete the base pattern and the base pattern is always included in every object.
Obscure Interaction Patterns - For deleting modifiers, it's weird that there's a number input for us to tell which pattern we want deleted, then have to click the delete button to remove that number. This can increase human error if they accidentally input the wrong pattern number to delete.
No Natural Mapping - Especially obvious with the "Pattern Number to Use" and "Remove Modifier" buttons not being right next to the pattern they are affecting.
Poor Excise - The user's mouse has to travel far from the pattern if they want to remove it or select it.
Why this design is good
Colour/Contrast - It's easy to tell which pattern we are selecting (highlighted in blue). It's also easy to tell which patterns are enabled and disabled due to the disabled patterns being darker than the enabled ones.
Modifier Grouping - It's easy to tell which patterns contain which modifiers since they're indented below the pattern. There's also a foldout so we can collapse the whole group to save space.
Reducing Human Error - Ability to Undo/Redo, as well as draggable sections and enabling/disabling patterns means designers can quickly change the execution of patterns in a simple click and fosters experimentation in user's interactions with the tool.
Representation of Information - Different fields now are visually represented based on the type of variable that would best match it. For example, the "Mirror" and "Shift" modifiers in the "Spread" pattern are booleans instead of a float. User can tick the box instead of having to input 0 (false) or 1 (true).
Pattern Grouping - All extra patterns are grouped inside the Draggable List section away from the base pattern to tell that any one of these can be deleted but not the base pattern.
Excellent Excise and Natural Mapping- To delete a specific pattern, to enable/disable a pattern, or to move a pattern, the user doesn't have to navigate their mouse far, it's just horizontal to the pattern.
Why this design is bad:
Medium Cognitive Load - There are many parameters still now in a more clustered environment (the list), however even though I've increased the visual load, I've greatly reduced the thinking load users would have due to proper representation of the data.
Iteration 1
Iteration 2
The backend of the tool uses a struct that contains:
Modifier Name
Modifier Value (float value)
Modifier Variable Type (enum)
Each Projectile Pattern is a 2D array of that struct, meaning we can add as many modifier to a particular pattern. Here's an example of how that looks:
A tool that displays all parameters of each boss, allowing us to quickly make changes to boss behaviours.
I had 3 main goals when designing this tool:
Make future expansion quick by programming a tool to simply create new bosses. The Finite State Machines should seamlessly incorporate this tool with as little steps as possible on the designer's end.
Make it visually appealing for the designers to enjoy tuning values!
Have all shared boss behaviour data be displayed in the tool
Since we were creating a boss rush game in a limited time, I proposed creating an inspector tool for the team that would allow us to quickly add new bosses and adjust their parameters. Thus, the Boss Profiler tool was born! Below are 3 of our game's bosses, each with their unique attacks, phases, throwable projectiles, and more! This is a tool that stores data, and the Finite State Machine (seen below) is where the Boss Profiler is referenced and its values used. This way, by simply changing the referenced Boss Profiler Scriptable Object I can completely change how the boss behaves! This allows for quick iteration and play-mode testing as the values don't reset on edit mode.
As seen in the screenshot below, or in the Boss Profiler screenshots above, each boss' Scriptable Object has a custom preview icon. I achieve this by using the override Texture2D RenderStaticPreview() function with Unity Editor Scripting. I create a new Texture2D for the dimensions, then grab the target's Profile Picture field and use that as the icon. This is the only way to have a Scriptable Object display different icons despite sharing the same script.
Here's a video of me playing with the Boss Profiler settings for one of our bosses.
Each boss has their own FSM, however the states are very similar across the board.Â
All of our bosses share similar patterns of throwing projectiles, so all the states except for the ones underlined in red are shared behaviours. This streamlines the boss creation process as we can copy over this FSM template and then program in the unique attacks.
Each FSM takes in a Boss Profile scriptable object (top right variable) and we use the values the designer inputted to the tool as the input parameters of the boss. This makes iteration efficient as we don't need to exit play mode.Â
It is best practice to use "Serialized Properties" when using editor scripting to set variable values instead of public getters and setters and referencing the base script. With Serialized Properties, accessing its value demands extra steps, so in the OnEnable function I first Find the associated variable in the base script and assign it.
I learned how to position custom shapes and labels in the inspector screen. The function Screen.width() is used to get the current width of the inspector even if it scales. For the y position of the rect I use GUILayoutUtility.GetLastRect() to get the y position after the "Boss Phases" title text, and then add an amount to set the position.
Enjoy these photos of us at the CNE 2024 showcase! We had an absolute blast there gaining feedback from players and seeing the public interact with our game. My highlight was seeing people draw themselves on our "Employees of the Month" board in such unique styles! A couple of people even went out of their way to concept new bosses that we can add into our game based around grocery-store themed items
Project Collaborators: Ashton Elliot McKenzie, Sarah Chambers, Kien Ho