top of page

OFF AIR

OFF AIR is a Boomer Shooter based on the game ULTRAKILL with a focus on weapon swapping and fast paced gameplay. Playing as Robot who is in a gameshow with other robots competing to escape the stage.

Platform |  PC                                     Engine | Unreal Engine 5         Duration |  4 Months                       Team Size | 5 
Playtime | 1 Hour                         Roles | Technical Designer, Combat Designer, Team Lead

Responsibilities 

​

  • Designed and Implemented Limb Based Damage System to take into account damage modifiers like headshots.
     

  • Utilize Animation Notifies and other tools in Engine to setup animations based attacks for the Player and Enemies.
     

  • Designed and Implemented all Enemy AI Interactions and Attacks
     

  • Designed and Implemented all Player Character movement and animations
     

  • Handled Performance Optimization tasks for certain systems
     

  • Utilized Editor Utility Widgets to make tools for other designers

​

​

Doing the Movement for the Player

​

As ULTRAKILL being the base game when taking a look at the player movement and the feel are a huge component in allowing the player to feel empowered in ULTRAKILL along with attempting to appeal to two of our pillars; Push-Forward Combat and Free Form Movement. I decided to replicate the movement best I can along with some key changes in terms of how certain actions chain together. 

​

In the video's above you see a showcase of the movement within the game which includes, Wall Jumping, Wall Clinging, Sliding, Dashing, Ground Pound, and Traditional Movement like Walking and Jumping. These movement mechanics are necessary in different situations to allow the player to use traversal as a defensive or offensive option when in combat.

How the Parry was done Right

​

Trying to create a projectile parry that felt good to players was extremely challenging as well as attempting to balance such a mechanic was very troublesome. Some of the issues that caused this was that it has to be balanced on multiple aspects of gameplay. The first step was the decision between making it anticipation based or reaction based. This had multiple view points on what felt better and we ultimately settled on reaction based as that made the barrier to entry lower but felt better as the feedback was instant as they pressed the associated key bind. 

​

Another crucial step was having to tune the reaction of the parry per projectile. The projectiles themselves are functioning under polymorphism to allow for some functionality to be passed down to children projectiles that have different functionality per instance. This streamlined the process when parrying as they all have the code from the parent to perform the parry so all I had to do was tweak numbers and scaled sizes of new projectiles. I also utilized blueprint interfaces to send messages from the player to projectiles. If I didn't the other way would be casting to those projectiles causing performance issues.

How the Enemies were brought to Life

​

In OFF AIR the enemies are your main obstacle and requirement when attempting to complete levels. I wanted the enemies to feel readable in terms of how they function as well as have specific counter play when you want to dispatch them quickly. They are intended appear simple to handle when approached individually but when encounters happen and multiple enemies are presented at the same time they allow massive change the approach the players take to clear a wave/room.

​

The first thing I wanted to focus on was how I wanted to streamline the process when creating enemies. So I utilize a base class that holds all major functions that all enemies will share. This includes the following:

  • Combat Manager - Handles the  Animations that are tied to the attacks and states that the enemy uses

  • Health Component - Universally used throughout the game for the player and enemies which handles the health, damage, and death events

  • State Manager - Keeps track of the current state the enemy is in and checks the states of things interacted with that share the component

Chaser - A very simple enemy that paths towards the player. When it is in range it will stop and leap towards the player doing significant damage if it manages to connect with the player. They also come with a low health pool and are considered fodder enemies. Even though that is the case when presented in great numbers they become a major threat as they clump when 6 or more are presented at one time.

​

I wanted the player to kite these enemies and typically deal with them last as they shouldn't be the main concern of players when they are presented in multi-enemy encounters. Notably when they are head shot they are instantly killed no matter the circumstance which rewards players who pride themselves on accuracy. In previous iterations we didn't it was difficult to get the player to use their verticality to their advantage but due to the Chaser's speed being slightly faster than the player you must at a certain point jump or change directions on the ground to disrupt their path. 

Tall Terry - The first introduction of a ranged enemy into the game. This enemy is supposed to be a primary target for the player in early levels due to their range capability and ability to target the player when in line of sight no matter the distance or height they also try to maintain line of sight if the player moves out of it. This is the only time they move. This enemy's counter play is using a weapon with a high rate of fire or burst damage which is limited to close range weapons typically. You want to hop from Terry to Terry when dealing with them. This makes them useful for level design in terms of leading lines for players in certain rooms. They are also the only enemy to be staggered when hit. Thus, enabling an emergent playstyle of tapping them with an instance of damage before they fire to reset the attack so the player can focus on more distressing enemies or obstacles. They also have a slightly larger health pool than the Chasers. Terry's place in the game is to fill in gaps that the Chasers cannot cover in certain levels using that ranged advantage.

Gunter - This enemy is a mix up to the players general gameplay as this enemy is a flying enemy who shoots a multitude of projectiles similar to a mini gun at the player. The twist is that on death this enemy will turn itself into a projectile that does massive damage to the player forcing them to either dodge or parry the projectile away from them. The purpose of this enemy was also to make the player move. Since they are bigger targets they are relatively easy to take down due to the large hitbox. Due to this aspect I decided to give him a larger health pool along with no headshot point. This doesn't cause a problem because the projectiles that he shoots are easy to dodge as long as you are moving at base movement speed.  

Ben Special Class Dover (BCSD)- This enemy is a ground based unit but mimics the same concept of Gunter but elevates it to another level. The main purpose is being a difficulty spike for the player as to keep them engaged. This enemy has a large health pool but has a ranged attack that mimics tall terry's attack but the key difference is that BCSD predicts the players movement based on velocity to attempt to hit a fast moving player. It also features a melee attack if you are in close proximity to him. This forces the player into a playstyle that involves weaving in and out of this effective ranged to bait the preferred the attack to capitalize on the window of rest the enemy takes after every attack. 

ROXANNE  - A Boss enemy meant to challenge the player. This enemy encompasses the mastery element of the player mechanics including movement and gunplay. I wanted it to move quickly but still come with typical boss health pool. Which presented challenges on balance. Players felt overwhelmed during early playtests due to very few times of rest. This being the case, Thus a taunt phase was added. After a certain amount of attacks the boss will enter a state where they don't move and shout a voice line at the player. This provides a window of time for the player to dish out lots of damage after avoiding the rapid bosses attacks which tests the players movement skill. It also helps balance out the bigger health pool as the boss wont attack when standing still.

Performance Optimization Efforts

​

When working on the game there were multiple times that we ran into performance bottlenecks. In a game that relies on gunplay and fast paced movement making sure that we maintained a constant framerate in most aspects of the game was important as any deviation would caused inconsistent feedback between the players inputs and desired effect. Tracking down these bottlenecks wasn't easy but I utilizing the Unreal Insights Tool helped a lot. 

Utilizing this tool helped to make some decisions on what was necessary to optimize in order to increase frames and reduce certain inconsistencies. Some of these include the Health Orbs, and Weapon Swapping. 

Health Orbs

​

One question that came up was how would the player regain health in these encounters. Previously if the player was in close proximity to the enemy while dealing damage they would be automatically healed. This caused an issue in enabling other playstyles in rooms that had a lot more room. Also due to the low TTK for some of the enemies players would often kill an enemy before they got to the effective range of the healing.

​

To combat this we decided on moving to a pip system where enemies would drop orbs that when collected by the player would heal them. This would reward players who were quick to dispatch enemies and still reward them if they decide to path towards the orb that was dropped. This did bring along a major issue. As the orbs were persistent so the player could return to them. Which in huge encounters many orbs would be spawned causing system slowdowns when many are spawned in the level. This is where we used object pooling so their are only 25 health orbs in any level rather than creating a brand new orb on every enemy death. 

Weapon Swapping

​

When it comes to swapping the weapons in the game we had to address multiple issues with how it was handled to manage the data. As well as have that information readily available for other elements in the game like the UI. This was also achieved by using object pooling for the weapons. But is also handled with a separate actor to separate the data from the player. This helps with tracking how much ammo is in a stowed weapon without relying on a separate actor to track weapon status. 

enemy Wave Manager TOOL​

​

During development there was a need for an enemy wave system to spawn multiple enemies in rooms. This utilized a room manager that you preset the wave information like which enemy to spawn, what wave to spawn in, and how many waves and enemies to create.  The initial system was great in terms of functionality as it got the job done. However, when it came to ease of setup and use there was difficulty.

Due to this I decided to utilize the EWBP (Editor Widget Blueprint) to give more control to the Designers on how they want to setup their spaces along with some extra quality of life features to make changing up levels easier due to encounters being changed constantly after playtests. This was done with also a UI that is easier to read and manage than the previous nested array setup.

​

Features Added:
 

  • Navigating to other managers in levels can be done from the Widget rather than locating them in the viewport

  • Ability to change the wave startup time per wave to allow for fast paced scenarios or methodical huge encounters

  • Replace Enemy Points rather than deleting an enemy and changing where the point is by hand the Widget will do it for you keeping the information you preset to that enemy previously

  • Change the health of the enemies per instance so some enemies could be customized to the levels needs.

bottom of page