Run + Gun = GURN
Run + Gun = GURN
ROLE | Technical Designer & Project Manager
TOOLS | Unreal, Miro, GitHub Desktop, Google Drive, Click-Up
DURATION | 8 Months
TEAM SIZE | 7 Members
Main Backend Programmer, handling the systems that handles Player Data & overall game state.
Responsible for Programming & Iterating on the GameManager.
Responsible for referencing and transferring data across all Levels.
Persistent Levels for easy Level Loading & Unloading between rounds.
Programmed Character Select.
Changed how systems instantiate the player for 2 gamepads only, for local multiplayer.
Programmed the Upgrade Acquisition System using Blueprint Interfaces and Event Dispatchers.
Made the Aim Assists for Major & Featherstep.
Made all Widget Animations to properly communicate to the player the current game state.
Made the following Widgets with CommonUI (don't use this for local multiplayer):
Character Select
Upgrade Menu
Victory Screen
Onboarding Screen
I wanted to be responsible for a lot of the game's systems in-engine, iterating on them and growing significantly as a programmer.
I wanted to make a game that had every system complete. In all my previous projects I've struggled to contribute in a way that made our game feel complete, this was my final chance to achieve that.
I wanted to pick up a new engine, after working with Unity for so long. I wanted to delve into C++ and use a lot of the systems inherent in Unreal to create the game.
This was the first feature I implemented for the game. It was an incredibly important and large system, so I knew I had to learn what I could use in Unreal to make this possible. The answer was the Game Mode.
After researching Unreal and learning of the Game Mode and it's auxiliary systems, I set off to begin designing the Game Manager.
An unfortunate crutch for the project was that we couldn't work with C++, instantly affecting one of my key goals, so I also had to create this system in Blueprints exclusively (and the rest of the game).
This kind of helped me in learning how the Game Mode works. So, after designing, I set out to begin programming. To view the original designs, click here.
I set up how Players spawn and randomized said spawning, how scoring is handled, how the Timer affects the match, how Items spawn, respawn, and even begin respawning.
With this we had a space to test our mechanics and gameplay to see if it was fun.
The GameManager was the first system I programmed, and the biggest issue with this is that it was heavy on dependencies and not built for modularity. It led to heavy refactoring and even then the same problems persisted.
So I vowed to make sure everything I built was as low as possible with dependencies and could be iterated upon easily.
I made a Testing version of the GameManager with less dependencies for our level designers, implemented Event Dispatchers and Blueprint Interfaces, and changed how a few systems work to be less dependant on the GameManager (Item Respawning became independent of the GameManager).
After that, a lot of my work with the GameManager for the rest of the project was implementing other systems alongside the GameManager (which was always a chore because of how the system was originally set up).
After our MVP, a design shift happened. We opted for a more arcade-focused feel for our game, with scrapping two more characters for our Upgrade System. One shift was levels now switching between Rounds instead of between Matches.
The original GameManager was not set up for data to be transferred between Levels. And I wanted to do this in a way that didn't ruin our games performance. After some research I came across persistent levels.
This was perfect because now all of our levels could exist in one Level, without ruining performance and without needing to do heavy refactoring for the GameManager.
I set up Actor Referencing so our Level Designers won't need to change their current workflow and had the GameManager use the Level Blueprint to switch Levels between rounds.
PERSISTENT LEVELS SET UP IN ONE BIG LEVEL
After dependencies from Character Select was finished, and setting up the GameManager to wait for animations so the player can understand what's going on, I'd say this was my most important system implemented, and one that I could have done better with.
I've learned a lot about how not properly setting up dependencies and instantiating actors really slows down and messes up our systems. I took that frusturation with me to the rest of the project to learn proper Design Patterns and use Unreal as much as I can to implement systems that work good and is easy to understand.
If I could work on it again, I would have done more research on functions inherent to the Game Mode, and implemented proper design patterns for this system. Still, I am proud of what I did with it, and you can view it completely in the video to your right.
This was my first introduction to Widgets in Unreal, which would be an introduction to a lot of pain and suffering with Local Multiplayer and Widgets.
Unreal is amazing for online multiplayer, it's great for single player. Not great for Local Multiplayer, especially for Widgets.
I was given designs for how it would work and set up a quick prototype for how it would work. An immediate issue I came across is Widget Owners and multiple players interacting with Widgets at once.
For MVP, I made it so that menu navigation was done manually in the PlayerController instead of using what was already in Unreal, because that was the only way I could make it work with 2 controllers at once.
This was heavy in dependencies, so I ensured my team that I would fix this and knew I had to learn some new systems to bypass dependencies.
Widgets have a way to reference their Owners, but not the exact Actor Type their owner is. And I needed information to be sent across Actors when buttons were pressed, and I didn't want to do this too many object references.
Blueprint Interfaces was used to gain references to specific actors when needed, without using Unreal's memory-heavy Cast system.
Event Dispatchers sent information without needing the root object to have references to the Actors they sent the information too, and this allowed for proper sequencing.
Widgets were still having issues with 2 controllers at once, so I made it take place in one screen while making the Button widgets owned by 2 PlayerControllers to use their Player Index, sending said info the GameInstance (this exists always across Levels).
After struggling with Widgets in Local Multiplayer, we opted to make our game work only with 2 Controllers. This was a painful but important change, as a lot of issues we had with our Widgets was with the Mouse overriding Controller navigation.
(I feel we could have worked around this using C++, but with one of our programmers not being able to use it, we had to stick with Blueprints).
I cleaned up how the BPIs & Event Dispatchers were handled, made some Widget Animations and used the Widget Switcher in CommonUI to switch between Player 1 & 2, creating an understable and easy to navigate Character Select.
After our MVP, we scrapped 2 new characters to make room for another scrapped system, the Upgrade System. In between Rounds, the losing player would be able to choose a card that empowers them somehow.
I did heavy research on how designers make and implement randomness into their games. I needed to know because with my original designs, I wanted to set up randomness in a way that could have changeable metrics for our designers.
The system got simplified, and I could just use RNG with shuffling arrays and removing chosen Upgrades from the array, so I was disappointed to see my designs not be implemented fully, still I'm glad with what I've learned from the research.
After designing the system, I wanted to figure out a way where,
A: designers could modularly create abilities without having to do heavy programming.
B: Have abilities be referenced easily without heavy dependencies.
C: Have this data be stored in a Widget in a way where it can send necessary information to the Player, to activate their upgrade.
My answer to this was Data Assets. I created a base Data Asset that set up variables on art used, text, and an integer to store which Upgrade needs to be activated (this reasoning will be explained later).
The Data Asset also had an inherent function that return all of it's variables, to be easily obtained by any Blueprint using the Data Asset.
An array of Data Assets is stored in the Character Blueprints for Major and Featherstep, which sends that information to their owning PlayerControllers.
I then use Event Dispatchers to send information to Player Controllers, and the Game State, to properly remove the Upgrade Menu and begin the match proper.
I wanted to use Actor Components that would be given to the Player, which would implement some code to change how the abilities work. But with deadlines approaching, the programmer working on the abilities themselves opted to use a switch case, which I obliged.
So each Data Asset has an integer that activates an Ability through a Switch Case. Abilities exist inherently in our Character BPs, they just need to be switched on.
With all the other features that needed to be completed after Alpha, I accepted that this is how the system would exist for the rest of development. It's still modular and allows for easy use for designers.
Players were constantly confused on who won a Round, who got an Upgrade, and remembering Upgrades chosen during combat.
A lot of these issues were solved with Widget Animations. This was nice, because you can use animations to trigger many things through their keyframes. Instancing objects, setting up references, turning on Inputs, as long as you had some code to run based on animation keys.
I also worked with our UI/UX Designer to display Upgrades chosen during gameplay, through displaying chosen Upgrades on the HUD.
The animations made can be seen in the right, alongside how Upgrades appear in the HUD.
I made the overall Game State understandable to the player through Widget Animations. Showing who gained a point once a Player was defeated, and animating a Victory Screen as well.
There's some more minor things I did as well, but I don't want to be here all day. The animations I did can be viewed down below.
Our game was designed with Keyboard & Mouse gameplay in mind from the very start, so we were hesitant to switch over to 2 Controllers. I knew it could be done with proper systems to help with the cursed problem that comes with Controllers in FPS gameplay.
Especially in a game as high speed as this, we needed to reduce this problem, and my solution was Aim Assists.
I knew Aim Assists existed for FPS games that used controllers exclusively. A friend came up to me and told me to research how Halo does theirs.
So I did my research (man there's a lot of good sources for this out there) and began to design how Aim Assist works for our game.
I took a lot from Halo's design but changed how Bullet Magnetism works (how bullets steer towards the enemy) to still inspire the Player to try and constantly aim accurately.
I also wanted to introduce changeable metrics in the system to allow our designers to fine tune the experience, especially since all of these systems are invisible to the player.
My biggest regret with the system is that I couldn't figure out the math to setting up the Cone Tracers, so I bought a plugin that handled that math for me. It was disappointing since I ran into the same issue in my previous project, My Grandma's a Hitman.
Major's our Gun Guy, so his Aim Assist system had to be the best since that's where a lot of players struggle with, Aiming with a Controller.
The system takes distance to the enemy player, and separate cones into account. Distance Cones exist to check if the Enemy is within a Sweet Spot range. If this is true, then Bullets always land on the Enemy.
There's 2 Cones for detecting the Enemy. The AutoAim FallOff and the AutoAim True Cones. FallOff will make it so that each shot landed against the Enemy will gradually return the BulletVector back to the center of the screen, to encourage the Player to try and Aim Accurately. This resets under 2 conditions: If the distance to the Enemy is within the SweetSpot Range, OR if the Enemy is within the True Cone, in which Bullets will always hit the enemy no matter what.
Images detailing the system more can be viewed down below. Of course, the Distances and Radius of all cones can be changed, alongside the SweetSpot range for the cones. The strength in which Bullets return to the center of the screen is also changeable.
Working on this was a lot of fun, it was nice figuring out the Vector math to achieve what I want.
Featherstep was much easier to implement. Since he's Melee focused, a lot of trouble players had with him wasn't aiming, but keeping track of the Enemy when they're right up against each other.
With this I opted to have a single cone for detecting the Enemy. Whenever the Player holds to swing, it'll turn the camera towards the enemy. The speed at which the camera turns is editable. Of course, the Cone is editable as well.
Before this Project, I was always scared to initiate programming, due to fear of failure.
But through taking initiative and putting in hours of research, learning and failure, I achieved all the goals I set out for myself (except C++).
There will always be constraints limiting what you can do, and what you want to do. Learn to work under these constraints, and try to achieve the same goals under said constraints.
Sometimes the Constraints you work under can allow for work to be done in ways that wouldn't work otherwise. How I used Persistent Levels and 2 Controller Exclusivity allowed me to set up systems in quick and effective ways.
Constraints can be great opportunities to get things done and focus your learning.
In how I set up the GameManager, it led to some issues that would persist in development. Had I not failed, my future features would have suffered similar problems.
Don't be afraid to fail, as you can use it as an opportunity to grow immensely. Besides, failure is inevitable, so learn to use it as a tool in your arsenal.