top of page
Screenshot 6.PNG

STEEL REAPER

About the game

Earth has been invaded by an unknown alien force. The fate of all of humanity lies within one man: STEEL REAPER. Shoot and explode your way through hoards of invading alien mechs in this third-person over-the-top action shooter. Collect points to upgrade your mech and save up to activate the Big Gun, destroying the alien mothership and saving humanity.

Steel Reaper was my final game project at DigiPen Institute of Technology. The game ran on Unity and and the team consisted of 3 programmers. My role was the AI Programmer and Designer. This role saw me working on everything AI related, from creating the behavior tree architecture to designing and balancing enemy behaviors.
Screenshot 3.PNG
One of the first systems I created for this project was our behavior tree architecture. I had previous experience working with behavior trees in Unreal 5 so I was fairly familiar with the basic structure of a behavior tree, but my biggest challenge was figuring out how to translate that logic into C#. Eventually, I created classes for trees, blackboards, and nodes, all of them abstract classes. The reason I opted for abstract classes is so that the base classes themselves could not be accidentally instantiated in the game. This also led to abstract functions being created for many of the base functions (like initializing the tree, creating the blackboard, constructing a node, etc.). This approach allows for an additional layer of protection when compared to using virtual functions since the lack of a default implementation means that each override is unique.

In my implementation, the behavior tree class is fairly simple. It holds the tree itself and controls the flow of the nodes as well as connects them to the blackboard. The actual tree logic itself can be created in either a single construction call for the root node or by adding leaf nodes onto an already existing root node. The simplicity of this class allows for behavior trees to be easily built and the complexity to come from the content of the nodes and how the tree is structured.

The blackboards for this architecture allow for variables to be added either via script or through the inspector window in the Unity editor. The blackboard supports many standard variable types (ints, floats, strings, bools, Unity GameObjects) and other variable types can be implemented as well in new child class instances of the blackboard. Variables can be edited in the inspector tab through the blackboard's custom dictionary structure, allowing for new variables to be added easily.

Behavior tree nodes have three major variables: state, parent, and children. The state determines if this node has returned success/failure or if it still needs to be run. Nodes also have functions to run logic on enter, exit, and interruption. Enter and exit logic are called each time the tree runs that given node, allowing for checks to be made (for example, is the player still within our attack range? Is this the first time we've run this node? etc.). The interrupt logic is run whenever the tree has to interrupt a node and it has not returned success or failure yet (for example, the player has now gotten within attack range of the enemy so the tree must interrupt the current behavior to run the attack sequence earlier in the tree). This allows the node to have a way of pausing/stopping/resetting itself if it's interrupted rather than potentially breaking the next time it is called.

Behavior Tree Architecture

Screenshot 5.PNG
The next major system to tackle was our spawning system. We needed to decide how encounters would look in our game, whether enemies spawned constantly, in waves, all at once, or one at a time. The first iteration of this system had enemies spawn all at once in waves that increased the number and variety of enemies the further the player progressed. While this approach was initially satisfactory, we found through testing that players could run into a corner and funnel the enemies into a straight line as they approached, trivializing combat. As a result, we are looking into changing up our spawning system to mitigate this. Our new approach sees smaller groups of enemies spawning in a wider variety of locations across the map, meaning they no longer all spawn in one location. We also want to have smaller waves spawn during the current wave in order to extend the action. As of writing this, this system is still in the early stages of development and testing.

Spawning System

Screenshot 6.PNG
Being entirely responsible for the enemies means that I am also responsible for designing their behaviors. Our team had decided we wanted 3 major enemy types: Beetle, Wasp, and Mantis. Each of these types has their own unique approach to combat. Beetle charges the player like a bull, Wasp flies around and shoots at the player, and Mantis has a large laser cannon that takes time to charge but does large damage.

While brainstorming at the start of the project's life, our team had many great ideas for enemy abilities and behaviors. We wanted the Wasp enemy to be able to block the player's missile lock-on and protect other enemies with a shield, we wanted the Mantis enemy to have multiple smaller machine guns that would attack the player if they got too close as well as a shockwave attack that would stun the player momentarily. I wanted to implement all these features and more, however as we got further and further in development, I realized that may not be within the scope of this project. As cool as those new moves would be, it would involve much more playtesting for behavior, enemy composition, and attack damage/frequency, among other aspects. This would require a lot of balanced work and time on my end which could be spent in other aspects of the game, such as adding animations to the enemies and improving upon the behaviors that are already present.

One of the most important ideologies I held when creating the base behavior for each enemy was that they needed to challenge the player to use all of the tools at their disposal. I didn't want to create enemies that forced the player into one approach ("this enemy can only be destroyed by missiles", for example) but I also didn't want each enemy to have the same weakness ("dodging always works", for example). In order to achieve this goal, I planned out what action I wanted each enemy to challenge the player with. Beetle was a fast-charging enemy but was ground-locked, meaning the player could easily jump over them before they charged. Mantis has a huge laser beam attack but once the attack is initiated, Mantis stops tracking the player, meaning the laser can easily be dodged. Wasp can shoot at the player, but the spread of their minigun is so wide that the player can easily out-maneuver the attack. While all these strategies are in no way the only method to defeat these enemies, the enemies were designed around these strategies in mind and as a result, encourage a more diverse playstyle.

Enemy Design

Screenshot 2.PNG
This file is the Behavior Tree script for our first enemy we call "The Beetle". This file mostly shows how a behavior tree is made using the architecture, with all of the nodes in the "SetupTree" function being custom logic.
 
This file is the base class for all enemy logic. It contains shared features like VFX objects, health, damage, and other stat variables, and spawn/death logic.
This file showcases the base class for all the nodes in the behavior tree. All leaf nodes inherit from this base class

Code Samples

bottom of page