'Indie Dev Story' Postmortem
The thoughts and opinions expressed are those of the writer and not Gamasutra or its parent company.
My name is James, I’m 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 on big scale games in a team and I use the smaller games to have some fun, learn and stay motivated through shorter development cycles. This is my first postmortem, I thought it could be interesting to others and useful for myself to reflect on the process.
Play the game here: http://bit.ly/2fZcWSI
What Is Indie Dev Story?
Indie Dev Story is an Indie Developer Clicker Adventure. It’s like a biography of some of the things myself and the other guys have been through, from sleeping on the floor in a basement flat to getting an office space and releasing a game to Steam. The characters in the game are based on the team I work with and the items that you buy are all things relevant to my journey!
Why The Clicker Genre?
I wanted to capture that frantic feeling of being indie and trying to balance everything; working on your game, paying bills, sleeping enough, staying fit and keeping up being social. I had played Cookie Clicker and Clicker Heros before and I liked their intensity but noticed that as you get further into the game interest can fade. I wanted to make Indie Dev Story a short and sweet playthrough, aiming for roughly 8 min from starting in the basement to releasing your game.
I aimed to make the game in a month, but things got pretty hectic at work and often I wasn't leaving work till 10/11pm 7 days a week. In the end, I made the game over roughly 2 months, making it in 1 or 2-hour chunks in the evenings/early mornings before going to sleep. This was a bit tough as I was tired and sometimes you need more than a few hours to get into something properly. However, at the same time, I think it was beneficial in other ways. Every time I started up the editor, I would play through the game and make notes with fresh eyes on things that would make the game better across the board (sound, UI, animations etc). I usually did a few of these smaller changes straight away as a fun way to warm up before hitting some of the bigger more time-consuming things, like adding a new mechanic.
Below is the original idea that I wrote down while on a train journey:
Engine & Art Style
I used the Unreal Engine 4 as I know it quite well now, it’s powerful and blueprints are perfect for someone who doesn't know C++! For the art style, I wanted to try something new. I went with a voxel style because I thought its clean simple look would complement the simple gameplay (2 buttons).
Below is some of the key reference that I used:
I looked into voxel programs and in the end went with Magicavoxel (https://voxel.codeplex.com/). I modelled in Magicavoxel and exported the meshes out as obj's, then imported them into 3DS Max so that I could tweak them slightly. There I turned them into editable polys, welded the vertices together and applied a harsh angle smoothing group. I set polys with different material ID’s where I wanted different colours to be. I found I could use UVW unwrap to select groups of polys based on their UV position, then collapsing the UVW modifier and hitting poly selection, the selection from the unwrap would still be there (This was a quicker than hand selecting each poly). Then setting the material ID number from there. Then I imported the mesh into Unreal and coloured it in engine using materials. I created a simple master material, which had a parameter for changing the base colour and then created material instances off of this. I used this method to colour ever mesh in-game with solid colour without having to do any unwrapping. This was great because it allowed me to change colours on the fly in engine, rather than reimporting and taking the time to texture uniquely. I thought about adding a low opacity paper texture with the base colour, but in the end decided against it as I would have had to unwrap everything to another UV channel.
The characters were made in much the same way. Modeled in Magicavoxel, rigged up with biped in max and animated with a few keyframes. Generally, I did 3 or 4 keys over 10 frames. Frame 1 and 10 were the same for looping, then depending on the animation, a key around frame 4 or 6. I tried adding keys for overlap and anticipation but the animation just got lost as everything was so quick. In some animations I held poses for a few frames to try to emphasise the movement, like with the weightlifting fitness animations. Without the hold it didn't look like it was a struggle. I also tried stepped interpolation of keys but preferred the movement of auto spline curve with a few tweaks here and there.
I used biped layers to try to keep the animation cleanly and in one max file. First creating a layer which was the idle pose e.g. “FitnessIdle01”, exporting that, then creating another layer on top, adding simple keys over the top for the actual movement e.g. “FitnessAnim01”. When complete, I turned the visibility off on those layers and started the process again for a new set. I made the two separate animations because of the way I wanted to play the animations in engine. In Unreal I did the moving around animation through the animation blueprint, using a 1 Dimensional Blend Space, going from an idle to a looping moving animation. I could have done the task animations through the animation blueprint state machines as well but I thought it would get messy so I instead used Montages. I would play the animation and then have it feed into a looping idle for that animation.
Example of 1 dimensional blend space (Top Image), Montage setup (Bottom Image):
In terms of the coding of the game, I used Unreal's blueprints system (like a visual code). Although it can get pretty messy looking, it is fantastic for people like me who don’t know C++. It's tough to show the code because it is across multiple places and functions making it hard to fit into a few image, so instead I'm going to try to explain roughly how/where I did certain functionality and some of the things I found useful. The core places I had code were: the Player Controller, the Player Character, the Hud Widget, the Buyable Widget, the End Screen Widget and the Ai_Character in the world.
The Player Controller
The Player Controller is the most important, it held all the time/date controlled functionality and this is where I stored most of my more important variables. It controlled the meat of the game; the game state, UI shop refreshing, lights turning on and off, freelancers coming and going, rent bills, achievement checking and more.
The Player Character
I used the Player Character to handle anything tied to the controls; filling the task bar values telling the AI characters in the world which animation to play (through montages). Generally, the Player Character sent information back to the Player Controller to be stored or would send information directly to the HUD to be shown.
Hud Widget & End Screen Widget
The Hud Widget is another important place for BP code as it is the front end and what the player directly interacts with. A lot of information travels back and forth between it, the Player Controller and the Player Character. I try to limit the functionality in here, instead making it more about manipulating the data to make things clear for the player. An example of this is using Append, to join values together to make them more explanatory, for example adding a '£' to the money as you can see below (note you can only call Append on strings). The End Screen Widget, has a slight more functionality in it. On construction (its creation) it grabs the values it needs from the Player Controller and Player Character and calculates the end scores and shows it there and then. I'm not sure, but I think this might be bad practice. If it was in the Player Controller it would be easier to access if I needed to, but as it was only going to be used this one time I thought it would be okay.
The Buyable Widget
As a separate widget is needed for each buyable item in the game, if I was to hand make a widget for each buyable item it would have been a nightmare anytime I change something. A trick that I use all the time in the office is whenever I have a widget that is going to be used more than twice I make a "sub widget". These can be complex to set up initially but are fantastic when they are working as it means you only have to change the master widget and it will affect all the instances of it! In the Buyable Widget I create an int variable that can be set externally (by clicking the eye icon on). Then use this value to drive the functionality inside the widget. Off construction I set up the look of the widget using a switch by int. Then off the button press I do the same thing, running a switch by int to feed the correct functionality, first performing a simple check to see if you have enough money.
Make a new widget with what you want:
Add an int variable that you can set externally (the eye icon). Then off construction set up the look of your widget based off of that:
Use the same int to switch functionality off of the button click (I have a switch on int my buy functionality function):
The Ai Characters
The main player, friends and the freelancers all work in pretty much the same way, although the main player is slightly more complex. I didn't use any behaviour trees only the Ai Move To node. The friends and the freelancers are told to spawn by the Player Controller. I use their event begin play to drive most of their functionality. They grab the appropriate location in the world and then use the AI Move To node to navigate there. On success, I rotate them to the correct direction and then kick off their functionality and looping animations. When it's time for them to leave, the Player Controller does a check for all currently in the world and tell them to finish work. They return to the start position and on success destroy themselves.
Example of the Ai Characters functionality:
Timer By Function
One cool thing I discovered and ended up using a lot was Timer By Function (https://docs.unrealengine.com/latest/INT/Gameplay/HowTo/UseTimers/Blueprints/). Originally I used tick, bools and do-onces to run looping functionality, however I read in lots of places that using tick should be avoided where possible as it's greedy. With the Timer By Function you can set it to call a custom event (looping if you want) after X amount of time, making it a lot more efficient. I used it in many places across the game, an example being the player mood bars in the bottom left. I set these off my begin play to loop continuously. You can pause these timers as well which is useful. The only problem I found was that you couldn't feed variables through them, but in cases where I needed this, I would just pull the information instead of feeding it directly into the custom event.
Example of the Timer by functions setup for the player mood:
Example of the custom events that I would call:
I started off getting a basic menu flow together and setting up the core controls and functionality; left click - performing task, right click - switching task. Then moved onto tying this functionality into the visual bars and getting an AI character to move around the world depending on the current task. From then on I slowly added new mechanics and tied them into the UI as I went along. Examples of the other mechanics were: time until launch, bills, fail game state, buyables, hiring freelancers, friends visiting, achievements, tutorial etc.
Below are some gifs from builds I cooked off as I made the game:
Hitting An Art Wall
After a while, I got to a point where I had a bunch of the mechanics working but the game wasn't really feeling fun anymore. Without the art, everything was feeling a bit rubbish. UI, models, characters, animations etc, everything was placeholders at this point. So I started on a first pass of the art; making a more detailed room, a bed, weights and a voxel placeholder character with some basic animations. As soon as I got these in the game started to feel fun again!
Putting Off Making The End Game
I had the game fail state in early on and I knew roughly what I wanted the end game win condition to be (releasing your game and getting a score) but for some reason I put off making it for ages. Bad development I know right?! I'm still not quite sure why I put it off so long, maybe I thought I needed more than a 2 hour period to make it. Next game I will definitely get the end game in earlier as it made it a lot harder to get a proper feel of the game.
At the end of the Indie Dev Story, you release your game and watch your steam game rating go up. To calculate this score, firstly I gave the player a value based on how far through development they got, then applied a random “market mood” value (0 to -10). Finally, I calculated your mood over the entire game and combined all the values together (if you had neglected your tiredness, fitness and socialness you would lose some score) and filled the comments based on how well you did. If you did badly you would get negative comments, if you did well then you get positive comments about yourself and your game. I looked at real steam comments for a bunch of them and used some actual ones that got on our game as well.
Getting A UI Look Was Tough
About three-quarters of the way through the project I hit another wall where I felt something was missing, the UI. I had been tweaking the UI constantly, but it was still all placeholder art and it needed a boost. So I started concepting out a new UI look. I found this hard as nails and it took me a few evenings but I eventually came up with what you can see in the examples below. I tried to make the UI look match the world, voxels with dirt and grime. As the player continues through the game and moves to better office spaces, I wanted the UI to look cleaner as well. I made three versions of each UI asset; Dirty, Mid and Clean swapping between them when the player switches offices. This was time-consuming as I had to grab a reference to each UI element in the HUD and end/fail screens and switch them to the correct version. I made one function in my HUD Widget so that I didn't have to set this up multiple times.
Zoomed out version of the change Full UI Look function:
The easiest way I found to get the look was to quickly model the shapes I needed in Magicavoxel, then render them out with transparent backgrounds. Bringing them into photoshop I then had a little pipeline of effects layers and dirt effects that I added on for each version needed; Dirty, Mid and Clean.
Concept of new UI look, in photoshop:
Final look UI:
An example of the three UI looks (as you switch offices):
Below is a list of the biggest problems I encountered and things that I think I could have done better:
Cooking Error Due To A Corrupt Struct
One night while trying to cook my game off it failed repeatedly because of a problem with one of my structs. I googled around and a few other people had this problem, I got around it by creating a new struct, replacing the references and swapping any references to the struct in blueprint to the new one. In my case I was using a struct array that held 2D texture references. I was using this so I could grab the defaults of a certain entry to fill the buyable image in in the UI. It corrupted twice so maybe I was doing something wrong doing it this way?
Issues With The Way I Calculate/Count Days
I used tick in the Player Controller to calculate the time of day and run a lot of the gameplay functionality. Tick runs into a timespan variable which stores the time in hours then I did a branch check to see if it is 11pm (23:00), if it was, I considered it a new day and run all the needed functionality. However, by the end of the project I found that this was not the best way of doing things. If you have a lag spike or are running lots of programs on your computer this method can easily fall out of sync and will continue to tick but will stop counting days as if 11pm doesn't exist. If I was to make this game again I would definitely look into other ways to do this. Possibly putting in a second-time check or remove the value from tick and instead run a looping timed function off of begin play.
Originally it always took the same amount of clicks to fill the taskbar which I found destroyed the pace and flow of the game, I completely didn't think about this when I came up with the idea. Luckily about the same time in our bigger game at the office, we came across custom curves (trying to expose XP requirement amounts to blueprint, rather than C++ to allow for easier balancing). I thought this was cool and tried it in my game to increase difficulty over time. It worked really well, X was clicks and Y was the amount each click filled the task bar. So as you got further into the game it would take a larger amount of clicks to fill the bar, meaning the player would have to buy items in order to fill the bar up more easily again. I ended up making multiple curves to be able to balance the tasks differently.
Difference Between Editor & Cooked Builds
We get this in the office on the bigger game I work on as well. The cooked version of the game sometimes acts differently from the editor version. For example in the editor when you finish a task your character will remain in the previous animations idle pose until you start a new task however on the cooked development builds the character stands back up into his default pose. In this case I think it's something to do with cooked version running faster than the editor version because it doesn't have to run the weight of the engine at the same time. The best practice I found was to cook builds off often, just to make sure nothing has completely broken.
Start Video Wouldn't Play On Shipping Builds
This was a random problem. The start video, my CbGamedev logo, wouldn't play on a shipping build unless I turned on: "wait for movie to complete". Also, there was talk on the forums that only 1280x720 mp4 25fps videos work (for windows), in case you run into similar issues.
Being Lazy, Easily Missed Optimisations
The game runs at an okay framerate but I know there are a bunch of places that I was being lazy and I could have made more optimised. For example in the UI I used a lot of UMG binds; tying variables directly to a UI element meaning it gets called/refreshed every tick. Instead, I should have created functions that would have refreshed these elements only when they needed to be. Also in some places I used Get All Actors Of Class and Get All Widgets Of Class a lot, when I should have used them then once and stored the results in an array, so that I didn't have to run the search again and again. I learnt my lesson and next time I will try to be more efficient.
Colours & Being Colourblind
Being colourblind (mostly red-green), colours were always going to be a problem for me. I used a few different techniques to try to and help myself out; Firstly, the Art Style I went for I used solid fill colours. Also I would find ref with a colour that I wanted, load it into photoshop and colour pick to get the HEX values and then use that value in engine and tweak it. Similarly, I would google HEX values for things such as "Gold Hex values". Then finally I would get my friends and girlfriend to double check my colours make sure I didn't have any glaringly obvious problems!
Wrist Pain From Testing The Game
This makes me laugh when I think about it now, but when I was near the end of the project, trying to balance the game, my wrist started to hurt from all the intense clicking to get a good score. I have no idea how the Clicker Heros team manage to test their game ha!
Things That Didn't Make It/Didn't Work Out
The idea with my smaller games is that I have fun, learn new stuff and use the smaller development cycle to stay motivated. This project started to take longer than I wanted so I decided to tie it up and move on. I might come back in the future but for now, I decided to leave out the following:
This was one of the bigger things I wanted to include but didn't get around to. Events were going to be time limited (e.g. 1 day). If you completed them you would either get a bonus or not suffer a negative effect. Ideas for event types were;
- Computer setting on fire - You can’t use it till you fix it
- Investment opportunity - If successful, you get a money boost
- Do a course - If you complete the course, you get a boost to money earned
- Being sick - You are unable to do anything till you recover
A Boyfriend/Girlfriend Mechanic
This is something I thought could be really cool but in the end I replaced it with the friends visiting mechanic. Socialness while being indie in general can be where many people take a hit (myself included). I thought it could be interesting to have a mechanic where at the beginning of the game you have a boyfriend/girlfriend and if you don't spend enough time with them over the course of the project then they break up with you, making the late game harder to restore your social bar. In the end I decided to not include it as I thought it would involve pausing the game to show text for the player which would break the flow of the game. However, I tried to keep the becoming less social aspect. It's quite subtle, but if you don't spend time with people when they come to visit you, the chance of a friend coming to visit you is reduced.
Originally, when you hired people to work with you I wanted to create a Team Morale/Team Mood mechanic, much like the player has (bottom left of the screen). The higher team morale, the better the work output. In the end I decided against it as the game was too short to make proper use of it and I thought it would make the UI too busy and wouldn't really add anything. If I ever revisited the game and made it longer this is definitely something that I would reconsider putting in.
- Controller Support - The main controls would be easy as they are only 2 buttons but I wasn't sure how navigating menus would work and didn't want to spend too much more time on it
- More Animations - I would like to have made a more unique animations for the buyables upgrade stages. Like with the fitness buyables, each stage adding unique animations as I think they add a lot of character to the player
- More Shinys - Across the board adding more, UI transitions, sounds, polish etc
- Html 5 Version - I know Unreal can cook to Html 5, but I have no idea if it would work. It could be cool but I would have to look into it
Now that the game is finished I can see a bunch of ways it could have been improved, optimised and made more quickly, but overall I’m happy with how it turned out. I had fun making it, learnt a lot and my friends I work with enjoyed it! I think it would be easy to carry on improving and expanding the game, but for now I want to move onto something new! One last thing, I made sure that I backed up my project files and my source files to a dropbox account often so I had a backup. I hope you found this helpful in some way!