[As posted on The Way of the Indie Game Developer]
Greetings everyone! Remember back when I mentioned I would do a post on refactoring some day in the future? You probably don't. Well that day is today!
I always try to keep my posts relevant to what's going on with eNVy softworks at the time and since we've assimilated refactoring into our weekly schedule I thought there would be no better time for me to talk about it.
First of all let me be clear on what this article is. It is not a highly technical article but it does contain some traces of code. It does NOT discuss code refactoring techniques since this is a very complicated and technical subject on which various books have been written.
What this article does is discussing the merits of refactoring as well as expanding the term to help you integrate refactoring into your projects. It also explains the essence behind refactoring to help you get started. It is not strictly about game development so programmers and software developers are also welcome to read on. Oh, and artists don't think I forgot about you. I will be talking about assets refactoring as well near the end of this post. Now let's get started!
What is refactoring? Let me explain it on simple terms.
Code refactoring is a process in which part of a program is changed into a more efficient ,or easy to read version of itself without changing the way it affects the rest of the program.
For example imagine a multiplication function as the one that follows:
int multiply_a(int a, int b)
int c = 0;
What this function does is taking two integers as input, adding integer b to itself a times and then returns the result. Anyone can see that this is a multiplication function. You insert two integers and you get their product as an output. What more could you ask? Well I am sure anyone who knows a little bit of programming wants to destroy me for writing this function. That's because there is an extremely easier way to do the exact same thing.
int multiply_b(int a, int b)
Using the multiplication operator of course! As you can see the function stayed the exact same black box as before. The contents of the box changed drastically though. That is refactoring. Making code better without changing its functionality.
Now you may ask why is the second function better than the first. Well let me explain... with points! Everyone loves points!
Fewer lines of code
As you can see the second function is 5 lines shorter than the first one. Refactoring can reduce the size of your code drastically. In many cases you may even delete hundreds of lines.
Why does that matter?
The less code you have the fewer bugs you have. It is easier to pinpoint a bug when you have 10 lines of code instead of a 100.
Smaller code also means easier to read code. It is really hard to understand old code. Do you know what's even harder? Having to understand a LOT of it.
Small code is really easy to modify. If you understand a function perfectly then it is really easy for you to change it slightly in order to suit your needs.
More efficient code
Time versus Space
If we lived in a perfect world we could have programs running in very high speeds while taking up no space at all. Unfortunately that is not the case. Most of the time you will have to choose one and sacrifice the other. It really depends on what your platform of choice is. If your target platform has a better CPU than RAM you should focus on making your game use less memory and more CPU. This is another complicated subject but if I had to sum it up in one sentence the following would be it.
Fewer operations mean your program uses the CPU less, fewer variables mean your program uses the Main Memory less.
This particular example is an exception to the rule since it made the program more efficient in both a sense of time and a sense of space. Generally you won't be able to do that but when you can you should! Our goal is to maximize efficiency in both fields even if we have to make one of them less efficient.
When talking about time efficiency you will see those three Greek letters a lot: O, Ω and Θ. Those are measures of algorithmic complexity. In other words they map how fast a function produces a result depending on the size of its input. Check this link on Big O notation to learn more about those.
As I said earlier I will not go full science-y on you guys. What you need to know is that the less basic operations your code executes the faster it runs.
So let us count!
When counting we will ignore input and output operations since those will be the same for both functions.
Let's start with multiply_a.
Defining a variable and assigning a value into is a total of 2 operations.(int c=0)
A for loop is way more complex. First of all initializing i is another 2 operations. In addition to that in every iteration the loop calculates one sum, assigns that sum into a variable and even makes a comparison between two numbers. Those are 3 operations just to get the for loop running. For each iteration of the loop the code inside executes another 2 basic operations.
The result here is that multiply_a executes roughly 4+5*a operations where a is the value of input a.
Let's see what multiply_b does.
One basic operation. One multiplication. No matter what the input is it will ALWAYS execute ONE operation.
As you can see even if a=0 multiply_b will execute 3 operations less than its counterpart.
If each operation needs 1 nsec to execute, multiply_a needs at least 4 nsec to execute, while multiply_b only needs 1 nsec. That makes multiply_b four times faster than multiply_a.
To find our which function takes more space just count the variables. multiply_a uses two variables while multiply_b does not use any. This means multiply_b takes less space in memory.
You should also remember that not all types of variables take the same space in memory. Complex variables like structs and objects take considerable more space. Try to always have the smallest necessary variable for each task.
Variable type size changes from language to language so you should search for your language's on-line to find exactly what variable types are the more space consuming.
Code is all fine and dandy but a video game is so much more than that. When I say that I mean it quite literally. After three weeks of development our combined project files (excluding engine files) boast an unimpressive 11.9 MB of size. Of those 11.9 MB our code takes ... 53.9 KB of disk space. Do the math. Our code takes approximately 0.44% of everything we created for our game thus far. The other 99.56% of our game are graphical assets.
If you think this will change favourably for the code as the projects progresses you are wrong. In fact quite the opposite will happen. Good code means universal code. This means that once you've written the core mechanics of your game only small snippets of code will be added. Assets on the other hand just pile up until the very end of development.
I hope I made it clear on how much space assets take.
So why is that important you ask?
Disk space is really important! I answer.
But why? you might ask again... I have a 2 TB hard drive and I am not even a serious gamer.
Well it kind of isn't important... when we are talking PC gaming. But we rarely do talk PC gaming lately. Right?
We just started realizing one serious fact about the future of the video game industry. The more platforms your game is on the better. Porting is now easier than ever. We live in the age of Unity people! If you can make your game available to 100% of gamers out there why won't you?
I hope we all agree that a 10GB video game won't make a PC break a sweat but it can have some serious issues when entering the mobile market. For that it is imperative to keep the disk size our game needs to a minimum.
Let's talk how you go about doing that. First of all I will not talk about different compression methods. Most modern engines will take care of this for you depending on your platform of choice. What I am going to talk about is something more essential.
Game development is a huge endeavour that can span multiple months and in some case even years. During such long periods of time thousands of assets find their way into your project. Obviously not all of those will make it to the final build of the game. More often than not you will not delete those assets the moment they become obsolete, inflating the project's size.
An even messier situation you can get yourself into is forgetting associations of those assets within parts of the game that make the final build. That can actually make your final game bigger than needed. Always keep an eye for obsolete or outdated assets inside your project. Delete on sight.
Larger than Life Files
Sometimes artists take certain... liberties when your needs are not specified to the letter. It is easy for artists to create high resolution assets and even easier for them to pass them to the programmers full size. To avoid this, communicate to your artists exactly the size you need your assets to be. Sometimes even then you will still get assets that are bigger than you need. It is not wrong or rude to ask for smaller versions. Just do it and you will save yourself a lot of space.
Another way image files can get out of hand is transparency. Obviously 2D art assets will have a transparent background. Sometimes they have way more of a transparent background than they actually need. It is easy to assume that since we cannot see a pixel it does not take any space. That is NOT the case. Transparent pixels are still pixels. Crop your assets to as small a size as possible.
If you want to make your life as easy as possible don't leave refactoring for later. I understand the enthusiasm of making something new and evolving your game but let us be honest, we do not want anything down our way to haunt us. Refactoring is in its own unique way polishing. You should not leave polishing for the last moment. You will have way bigger problems approaching launch and you will probably discard refactoring as unimportant. It is not unimportant as I hope this article demonstrated.
My personal suggestion is that once a week get one programmer and one artist to look over all assets and code of your game and refactor as necessary. Week to week this job will become easier. Even though the size of the project increases, once something is done it's done. You will rarely have to refactor an asset or script twice.
If you are still not convinced refactoring is worth your time let me show you something.
This is a screenshot of all our project files since we started our new game 3 weeks ago. The versions that end with an r are simply the previous version after refactoring.
Notice a pattern?
Yes project size can drop THAT much. Now imagine if we never refactored. In just three weeks of work we would have an excess of approximately 21977 KB or 85% more project.
Imagine how out of hand this could get after a couple of months. Or a year...
Refactoring is a necessity to reach the best version of your game. Don't look down on it. Refactor as often as you can and you won't regret it. Saving that one MB today may save your life tomorrow.
Thank you for reading, leave your opinions bellow and as always stay focused!