'Rage' - Development Postmortem
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.
I’m James, an indie game developer (@cbGameDev). I make bigger games with my friends, in a studio I helped start up and make smaller games on my own in my spare time! I enjoy the challenge of working in a team on big scale games and use the smaller games to learn, have some fun and stay motivated through shorter development cycles. This is a postmortem about making “Rage”, I thought it could be interesting to others and useful for myself to reflect on the process.
WHAT IS RAGE?
Rage is an 80s style beat-em-up, inspired by Street Fighter, Marvel Vs Capcom and Mortal Kombat. It has a single-player game mode in which you play against AI, unlocking new playable characters as well as a Player Vs Player game mode. The characters in the game are made up of me and my mates.
WHY MAKE THIS GAME?
Before I started Rage I knew little about the mechanics of fighting games. At that time we had started playing a lot of Marvel Vs Capcom in the office and I was interested in how the game seemed simple on the surface but was hiding lots of complexities behind the scenes. I thought it would be a good challenge to try to make my own fighting game and I wanted to have me and my mates as the characters in it. Below is my initial rough idea:
The highs and the lows
Originally I planned to make Rage in my evenings over three months, keeping to my short dev cycle……but this didn’t happen. Instead, the game took close to six months due to a mix of expanding the game’s features, a time-consuming art style as well as being busy with big updates at the office. Many times I was tempted to stop working on the game; I didn’t like it anymore, it was taking too long or I wanted to start a new and exciting idea instead. However, my personal philosophy is to stick to it until the end, as finishing a game itself is a skill that needs to be worked on and now I am glad that I did push through to finish it. This tweet was posted by @GameDevLeague, while I was making Rage and sums up perfectly how I feel.
During a project, I write down all the other new ideas I have and while I continue to develop that project, a sort of game ideas natural selection happens. I end up dropping some ideas, others mature and some join together to create new ideas, leaving me with more interesting more refined ideas.
Below are gifs from the different builds I cooked off, showing the game’s progression:
Early on I found the video Concepts Every 2D Fighting Game Player Should Know by YouTuber GuileWinQuote. This was really useful to help me understand the mechanics of fighting games. I thought it was important to understand them if I didn’t include them all. Modern fighting games are getting more and more complex behind the scenes, they look and feel great but replicating this by myself would have taken far longer than I wanted. Instead, I chose to make the gameplay in Rage a more realistic street fighting style, no double jumps, flying or super fancy specials. This worked well with the 80’s theme and meant that it would not be too complex to develop. I thought having a single punch and kick would give too little variety to make the combat interesting. So I created a simple combo system to add a layer of complexity; with fast light attacks, normal attacks and slow heavy attacks. I carried this across to the three attack types I had; punches, kicks and crouching punches. The player also had a single special move. This took each character’s attack move count from 2 to 10, each with their own speed and damage. I talk about the technical side of this in the Code/Blueprint section later on.
Responsiveness is key
One of the key things I learnt from making the combat was that responsiveness is key. As soon as the player touches a button the character needs to begin their attack. It is the same for when a character returns to their idle pose after a move has finished, they need to snap back quickly else it will feel unresponsive. There is little time for anticipation in the initial build up or for overlap on returning to idle. To give other players opportunities to attack, I would pause the character at the end of their swing for a fraction of time, before the return to idle begins, almost as if a cooldown. This seemed to work, making players feel as if it was their fault if they got hit, rather than the game being unresponsive.
Expanding gameplay features
Initially, I was only going to make a player Vs player game mode but early on in development it felt like some sort of overarching progression was missing, so I decided to add in a single-player tournament mode. I locked the PVP mode and all but one character, instead players would have to unlock them by completing tournaments (as Super Smash Bros does). This felt much better, giving a challenge to overcome but at the same it time increased my workload as I then needed Ai, new UI and a progress saving system.
Balancing the game took time as there were a lot of values to play with across multiple characters. There were simple things such as health, speed as well as more fiddly elements such as the number of frames, damage and playrate for each individual move. Even small frame addition/subtraction seemed to have a big influence on responsiveness and ease of attacking. I tried to start balancing the game early on as I thought it would be a very iterative process, taking time to find good values that worked. As testing was the only real way to gauge if tweaks to values were making a positive impact on the gameplay, I tried to test/balancing each time I opened up the editor to work on the game. I would make notes then make changes and then play again with those changes. Another really useful method was to ask my friends to play it while I took a step back and made notes. This helped to not only refine the UI and flow of the game but for balancing it allowed me to see flaws in the combat; over powered/underpowered moves, dodgy collision, damage trace problems etc. For balancing the AI, I kept their health, speed and moves exactly the same as their player counterpart. Instead, I balanced them through their difficulty settings which you can read about later on.
I knew I wanted to have an 80s theme from the get-go and I also thought this would be a great opportunity to try using photos/images of my friends and treating them in photoshop to create 2D characters sprites (a style I had wanted to try for a while). This also allowed me to play with a slightly choppy animation style. When first thinking about the art of the game, I gathered a few images to use as key reference. This art summed up the direction that I wanted to go, in terms of colour palette, shape language etc, which you can see below:
General game look
Initially, I placeholdered all art (from characters to UI) while concentrating on the game mechanics. However, when the mechanics were more solidified, I returned to thinking about the visuals. I decided to make a style sheet and a visual gameplay mockup to help keep/consolidate the art direction that I wanted to follow. After gathering more reference images I set about trying to create something. I found this tough, especially the colour choices (as I’m colourblind), but I pulled something together which I thought would work well. This is the first time on a personal project I did this and it really helped, especially later on when it came to adding new unplanned UI elements. I will definitely do this from now on as soon as I know the game mechanics will work. Below are the style sheet and the gameplay mockup:
Rage’s game flow and mechanics were completed relatively quickly, however, the art style for the characters is certainly one of the reasons the game took longer than expected. At the end of the project, there were just over 3,500 character sprites (including background characters). With each main character having 40 actions/moves; walk forward, jumping, hit reacts, combat moves etc. Even with a green screen and Photoshop actions to help automate the process, I still had to hand adjust each texture, which was very time-consuming. I did a test early on trying to decide how pixelated the characters should look. I liked the idea of them being pixelated but at the same time I wanted to be able to make out details, otherwise, there was no point in having me and my friends be the characters. Below are some of the iterations that I went through:
Styling the characters
Here is my end process for the characters:
First I used an iPad on a stand to video record a person doing a movement from a flat angle against a green screen. Then I loaded that video up in Quicktime on my PC and selected a range of frames that I liked, cutting away extra unneeded footage. I then batch exported each frame as a separate image.
After that, I made a new Photoshop file and brought in every fourth frame image (to get the slightly choppy animation style). I then had a pipeline with separate stages as I wanted to make a nondestructive process, in case I needed to revisit/touch up stuff. Firstly I grouped these images into a folder called “Clean”, carefully cropping and deleting, as much of the canvas as possible, to save on hard drive space.
Then the time-consuming part started. I duplicated the clean folder to a ‘Masked’ folder and then from each frame masked out the background. In early placeholder tests (before the green screen), I used the quick select tool, which was a long and arduous process. I ordered a green screen off Amazon and instead used ‘Select-> colour range’ to remove the green from the image. I automated this with a Photoshop action but it still took time. I had to go through each frame afterwards and double check as even a single pixel outside of the characters area would auto generate collision in engine and mess with the gameplay.
Once happy with the masking I duplicated the folder to an ‘Edit’ folder and ran an action to add the crunched 80s style effects. This consisted of:
- Adjusting the levels to create greater contrast
- Using posterise to reduce the overall amount of colours
- Transforming the image to 50% of its size (with nearest neighbour interpolation on)
- Then returning it to its original size 200%
After that, I duplicated the group one last time, adjusted the canvas to be a power of 2 (e.g. 512×512) and scaled and positioned the character in the images ready for importing into engine. I also added a low opacity gradient going from the feet to the head, making the feet darker and drawing attention to the characters face. I then exported each frame as a PNG and they were ready for the engine.
Evolution of recording
I adjusted the way I recorded the characters as I progressed through the project. Initially, I recorded footage in my kitchen (1), but this made it very time-consuming to mask out the background so I bought the green screen (2). I then realised that I needed a fixed flat angle and more space to record in, so I borrowed my friend’s iPad stand and changed filming location (3). The green screen was working well but was a touch too short so I got hold of another one to cover the feet area(4). Overall this was a slow iterative process. I wish I had the end setup from the very beginning as it would have saved me so much time and helped to keep everything looking consistent, but live and learn!
As I progressed through development I ended up with inconsistencies between characters because I was learning and improving with each character I put in. This can be seen in the differences between Camille’s and Mitch’s characters. Below are some of the core things I learnt:
- Use a power of 2 texture size. This will allow you to compress the texture in engine reducing its memory cost dramatically and making the difference between a game running or not at all (In Rage Example being 350kb down to 85kb)
- Bring the images in at a good texture size, you can always mip it down in the engine (In Rage I brought images in as 512×512 and mipped them down by 1)
- You can set the pivot location in editor per sprite. So if a character’s hand goes out of frame, rather than increasing the texture sheet size to fit it, instead move them over in the texture space and use the in engine pivot to line it up with other frames
- Make sure there aren’t any pixels outside of your character, even a single pixel would get collision, messing with players hit box area (I was auto generating shrink wrap collision per sprite)
- My Photoshop actions deleted themselves mid development, so make sure you back them up by saving them to your hard drive (found out this is quite a common problem)
- Use a green screen if you are going to mask a large number of images from their background. Also, use a flat angle and try to film under the same lighting setup if you have to split filming into separate sessions
- Personally, I found it easier to work in batches. For example, with each character I would Quicktime select and export the frames I wanted, then I would do all the Photoshop work in one and finally do all the engine work in one. This won’t be for everyone but I found I managed to get into more of a workflow like this
I used the Unreal Engine 4 for Rage and I wanted to push myself to learn and use C++ instead of only Blueprints. I started off with good intentions, setting up a project from a C++ base and opening the engine but that was about as far as I got, haha. I went to set up key inputs in C++, but I had no idea how to do it, I started searching but gave up shortly after as I knew it would only take two seconds to do in Blueprints. In the end, I stuck to Blueprints and I think it’s a good thing I did otherwise the project would have taken even longer! I’m still adamant that I want to make a game using C++, but I think it will be more of a natural learning curve. For example, I know some localisation functionality can’t be accessed by Blueprints alone so I would have to use C++.
A useful new thing I did for this project was utilising the ‘Blueprint Library’ to make a more informative print to screen. Blueprint Library functions can be accessed from any Blueprint. With my game’s more advanced game mechanics I left in prints until the game was closer to being finished (e.g. the combo system). If you forget where the variable value (such as a non-informative float or int value) is coming from it can be annoying to find. So in the Blueprint Library, to save me time from repeatedly making it in different Blueprints I made this simple function, which allows you to give a little bit more context to your prints.
The combo system
Here is a quick run through of the logic I used to develop the combo system. Before I could perform any moves I needed to know what the actual moves were; which sprite sheet they would trigger, how much damage they would do etc. To do this I created an Object called player_move to store information about each move (see image below). These now hold some redundant information such as “which move” but a lot of it is still relevant such as; “MovesNeededRevers”, “B is crouch move”, “Damage to do” etc. I stored a list of each player’s available moves in an array in their respective player characters Blueprint so that I could access it later.
The next step was to find out and record which input the player had pressed. Off each input action, I performed checks to see if an input was allowed, if it was I ran a function to record the input that had been pressed.
This Input Pressed Record function took the input that was fed into it, turned it into a predetermined string value and then added that to an Array of last moves, unless the moves array length was above three. In that case, it would delete the 1st array entry (the oldest move that had been performed). Once this had all been complete I ran a function to determine what move should be performed, if any.
This function was called check move list for attacks and it did exactly what it says on the tin. It would then check the Array of last moves against the list of moves available in the player character. If an entry matched I would pull the information I needed from the move to be used later on, set the sprite to play and activate the damage traces.
I also set a looping function to delete the current move array and current move. This allowed for more responsiveness in the move detection.
For the damage tracing, I had two sockets on the player that followed the punch and the kick motion. These were set individually through the sprite sheets. When the damage traces were activated they would run the function below on tick, until the move was over or they hit the enemy player. Each tick they would check for an overlapping actor of type player_character. If successfully overlapping they would check it was another player (not self) and if it was it would turn tracing off and send the previously stored move data to that other players take damage function. The player who was hit would then calculate their health loss based on if they were blocking, crouching etc. This system definitely has its floors but it seemed to work okay for what I wanted.
AI that fights
I realised if I was going to have a single-player mode I was going to need an AI to fight back. The AI didn’t need to be super complex and I wanted to keep them clean and simple so I decided to keep the logic in the Ai_Character Blueprint instead of using Unreal Engines AI tools (behaviour trees etc). Rather than starting from scratch, I created a child from the main character for the base of the AI. This meant that I could use all the work I had already done in terms of the health logic, sprite playing, combat moves etc.
The AI needed to be constantly aware of what was happening in the fight, regardless of if they actually reacted to it. So firstly I setup a function in the Ai_character, off tick that would check certain variables in the human player: is crouching, is attacking, Location etc (Off tick was greedy but as not much was happening in the scene, I hoped I could get away with it). Later on, I would make use of this information.
External to the ai_character I made an ai_difficulty object, storing float variables which I could use to drive the difficulty e.g. chance to block, chance to move toward or away, chance to taunt etc. The most important value here is the AI thinking speed. An example of the Tournament 1 AI settings:
When the AI spawns in it checks and sets its settings from its assigned ai_difficulty object. Then when its Event Begin Play is triggered it kicks off a looping timed function. I used this as a base level for the AI difficulty. The quicker the timer the faster the AI can think and make decisions; should I block, should I attack etc.
Next, comes the decision making part. When the make decision function is run (from the timed looping function above) the AI calculates the distance to the player and then decides what to do on a case by case basis. I made four distance ranges: punching range, kicking range, far and too far. Each of these has a set of moves that can be carried out, for example too far has either taunt, move forward or do nothing. Whereas at a kicking distance the AI can choose from move forward, backwards, kick or block. Within these different ranges, I used a random float chance to drive bool branches based on the setting from the ai_difficulty object fed in on its creation. I found that with only a handful of variables I was able to create interesting and difficult AI. This system was easy to expand on, as I could just add in another branch check, as I did when I got the AI to perform specials moves.
THINGS THAT DIDNT MAKE IT/DIDNT WORK OUT
The idea with my smaller games is that I use shorter development cycle to stay motivated. Rage was taking a lot longer than I wanted, so in order to get it finished, I decided to drop/not explore some features that I thought could add to the game.
At the moment I clear the player’s current move list (list of their most recent inputs) on a regular basis (o.8 of a second). However, even so, there can be slight pauses and a chance to miss players input in-between clears. If I really wanted to make gameplay run smoothly, I would like to look into implementing move buffering: Storing the player’s next move during their current move. Move buffering is a common mechanic in fighting games and can be used to great effect to create fluid gameplay.
More advanced AI
In the end, the AI worked out okay. I could get enough variety out of how they fight to make them interesting, nowhere near as interesting as fighting a real player but good enough for this project. If I was to expand on them in the future, I would like to make the AI remember the moves players had made. Then use this information to catch patterns and adapt to how the player fights. E.g if a player repeatedly attacks with one move, using that information to affect the weighting in the AI’s decisions as to what attack they should use. However, this would not be simple and it would mean reworking and building upon the system that is in place. For this, I might use Unreal Engines AI tools instead but for now, it is a bigger job than I wanted to do.
I toiled with the idea of having a recharging stamina bar with each move removing a set amount of stamina. This is something that I have not seen in a fighting game before, instead it is more commonplace in the survival genre. In the end, I decided to leave this idea for another time as the project was already running long and I wasn’t sure it would work. As pausing to recharge your stamina bar could break the fluid flow of gameplay.
Cutting attack moves
For each character I actually video recorded a jump kick, jump punch and simple grapple/throw move. In an attempt to finish the game I cut these attack moves as I would have had to implement new systems to get them working. In the future the grapple/throw might be something I think about putting in the game to combat too much blocking E.g. you can successfully grapple and throw if another player is blocking.
Four player mode
I thought it could be interesting to have a tag team style gameplay 2 Vs 2 players game mode, with recharging health when not in the match, like in Marvel vs Capcom. However, I initially built the game with 1 Vs 1 in mind and was going to add in the extra player functionality at a later date. In the end, I dropped this as I left it too late in development meaning I would have to rework a lot of systems, spawning, UI etc. If I had built the game up from day 1 with this 4 player gameplay in mind, it would have been less hassle than reworking a bunch of system at the end of the project.
Although my motivation for making this game went up and down, I’m glad that I pushed through to finish it and now I have even more respect for fighting games, especially things like Injustice Gods Among Us 2 which is so fluid and polished. If I was to make the game again I would make sure to use a green screen from day one and I would also think more about the characters’ clothing choices, making them more 80s-themed. However, even though I have a good pipeline down now, I would think twice about making a game with this art style again, simply because of its time consuming-ness. Overall I had fun, learnt a lot, my friends at the office enjoyed the game and I hope other people will do too.
On to the next game!