DevLog #9 – How to optimize your game made with Godot (1/2)
This week, let’s put gameplay design aside and focus instead on some technical considerations. I will talk about how I optimized Destroy The Monoliths before the alpha release.
Since this post is a big long, I decide to split it into two parts. I will first give some general tips about the process I use with the Godot engine. Then, in the second part, I will share more details specific to the genre and art direction of my project. If you are making your own game, whether it looks alike Destroy The Monoliths and uses the same engine or not, I hope you will find this devlog helpful in some way!
Disclaimer: this is not an extensive guide about optimization and I do not claim to be an expert. This post aims to share my opinion and humble experience on this topic, as well as some of my gamedev learnings. You are free to disagree, and what works for me may not work for you. If you wish to share a different process or some other tips, I’d be glad to discuss with you in the comment section 🙂 Also, I strongly recommend to read the official Godot docs that give plenty of great advice on optimization!
Part 1 – General process
When to optimize?
If you are still prototyping, spending time on optimization is a bad idea because you may suddenly decide to get rid of many systems and features. It seems more relevant to wait for the core mechanics and the code base to become more stable. Optimization can be a tricky process so you don’t want to waste time. Also, optimization will probably make your code less readable so you want to make sure you are happy with your current project organization. Things could become harder to change or refactor afterwards. Optimization can introduce new bugs too. If something is working properly, you want to avoid messing with it unless it becomes really necessary.
On the other side, I believe you should not delay optimization until the very end of the development. If you intend to release a commercial game, you need to provide a build with decent performances (given the minimum configuration you stated) otherwise customers will (rightly) be unhappy and refund it. You don’t need to have a perfectly optimized alpha build, but it is better to have a look at the topic somewhat early. You want to predict some of the bottlenecks and check how confident you feel about your abilities to get the performances good enough in the future. Don’t underestimate too much the time this can take in your roadmap. Game projects can easily get out of scope and turn into a never ending process of polish / reworks / optimization. Make sure you have a rough idea what you are going for.
Identify an issue
It’s time to optimize. First, you have to test your game and see if you notice a performance issue. Maybe you have occasional lag spikes when enemies spawn. Or maybe the FPS is continuously terrible. Or maybe the game performances keep getting worse over time. Very much like debugging, try to get a reproducible situation where the issue happens. For this, you have to make hypothesis about the problem and test them. Trust your intuition. But be also prepared to be wrong. Sometimes the issue comes from unexpected places.
Do not hesitate to intensify the issue. We usually do this with stress tests. Spawn a huge ton of enemies and bullets, generate a huge map, place automate buildings everywhere, etc. The more load, the more visible the issue becomes so it is easier to notice the cause and measure the impact of your future solution.
Pinpoint the bottleneck
Once you have a reproducible scene, you want to strip it as much as you can. Remove things and see if the performance issue is still there. Godot is great to do this because you can change things in the editor and the inspector, and the running build gets updated live. The goal is to pinpoint the main root cause. You want to fix first what has the most impact. Optimize only what really matters.
Have a look at the debugger tools in the editor. For instance, you can detect memory leaks there. If the number of objects and nodes never goes down while it should, some things are probably not freed properly.
When the FPS is low, what I like to do is first hide all nodes. If the FPS goes significantly up, then it is maybe a rendering issue. Disable things one by one until you find the one action that has a significant impact. If hiding does nothing, this may be a code issue. Use the profiler to spot functions that take a lot of computing time. Often, these are the ones called at each frame. In the scripts, add
return keywords or comment lines until you spot which operation is expensive.
I guess this process is a kind of binary search. You disable things one by one and check if the problem persists or not. Then you dig deeper in one branch or the other until you find a root cause you can deal with.
Fix the issue. Then move on or restart the whole process again
Now here comes what can either be the easy part or the hard part depending on the issue. Some solutions are obvious, others can require a lot of work. Read the next part to see some of the solutions I used for my game Destroy The Monoliths.
Always test your solution with some concrete measures. Proceed likewise to fixing a bug. Run a reproducible setup and see the issue. Change a few things. Run the scene again and see if it got better. If you have no way of measuring the impact of your optimization, you risk wasting time.
Now, let’s say you solved the issue. Congratulations! You can now decide the performances are good enough and move on to some other task. Or you can restart the same process all over again. Work on a different issue. Or maybe on the same issue again. You can now intensify the load further and pinpoint the next most-impactful root cause. And then the next. Etc. Just don’t forget that you have to move on and release the game at some point.
Part 2 – My experience with Destroy The Monoliths
I hope all the previous explanations give a clear general guideline to start optimizing your game. In part 2, I will talk about some concrete solutions I used to optimize Destroy The Monoliths. I will also give more details specific to Godot.
[…] If you didn’t read part 1, start here! […]