As software developers, we spend just as much time, if not more, debugging and profiling our programs. Having good tools to make these processes easier is important. Luckily, YoYoGames overhauled its debugger and profiler, making it more robust and easier to use.
In this post, I will explain my process when debugging my games, and some tips to find bugs quickly and identify bottlenecks in your game. I will try to cover as much as possible on these tools, but I will most likely miss a few points, given it is such an extensive topic. It’s a good idea for you to experiment with all these tools yourself and figure out ways to make your debugging experience easier and better. Reading the official documentation is also a great way to learn more about it after you’ve read this article.
We’ve all been in that situation where we write code, run the game, and nothing works like expected. Then we go back to the code and look at it for 20 minutes thinking it should work. The first solution that comes to mind is to check the values of variables, or the results of conditional statements, by using debug messages or drawing text to the screen.
This is fine for quickly debugging a small feature, but when you have many moving parts working together, generating these messages can quickly get out of hand. This is when a debugger becomes useful. GameMaker’s debugger allows you to run your code line by line, checking the values of every variable and their changes along the way. It can also show you more advanced information, such as the state of your textures and surfaces, current graphics options, values of buffers, etc.
Profiling is often more useful in the second half of the project when you experience slowdowns in your game due to the amount of objects interacting with each other. Programmers are decent at spotting obvious bottlenecks, but on any application that isn’t trivial, it’s hard to find them. This is where the profiler comes in handy, giving you detailed information about what functions are being called at every stage of the game run-time and compiling the data in a way easy to analyze. Once you pinpoint the function/section that is running slowly, then you can optimize it.
Let’s start with the basics. You may be used to launching your game by pressing the Play symbol at the top bar, or by pressing F5. However, to launch the game with the debugger attached, you will need to press the Bug icon instead, or you can also use F6.
Once the game launches, you will see a new tab show up at the top of your code editor, and a multitude of other windows opening. You can customize the layout of every one of these windows, but here’s what mine looks like:
If a window isn’t showing up, or you closed it by accident, go to Debugger → Windows and there will be a full list of available windows that can be displayed.
Once you are in the Debugger tab, you will see a set of buttons at the top. Let’s go over each one of these, and we will go into more detail as we go along in the article. This is what the toolbar looks like:
From left to right, we have these sections:
Continue, Break, and Restart Game: These buttons let us pause (break) the program, continue to the next breakpoint (or continue execution if no other breakpoints are set), and restart the program.
Close the game: This one acts similarly to the one above to stop the execution of the game.
Step into, Step over, and Step out: These buttons advance our code line by line when the application is paused. You will use these a lot.
Real-time debugging: This is a toggle to control whether we want variables to be updated in the debugger while the application is running, or only when it is paused.
Discard collected data: While debugging, the debugger will keep the data it collected on its last run, so you can reference it after you’ve closed the application. This button will discard all that data.
Memory, FPS and Colored Circle: Displays the current memory usage, current frames-per-second (FPS), and the circle will be green when the debugger is attached and the application is running, or red otherwise.
Now that you have run your game with the debugger attached, you may have noticed that not much has changed. You still can’t see the values of variables and the only thing that seems to be working is the graph. The reason is that debuggers need to pause your application to inspect the values of variables and buffers (there is a way to do this in real time, but we’ll get to that later). One way to pause your game is through the debug toolbar, by pressing the Break (Pause) icon. However, this will pause the game at whichever point it was at when you clicked the button, so it is essentially pausing at a random location you can’t control. A better way to stop the application is by using breakpoints.
There are three ways to set a breakpoint on a specific line:
Left-click on the line number gutter, on the line you want the breakpoint on.
Right-click on a line, then click Toggle Breakpoint.
Select a line, then press F9.
Remember that if you set a breakpoint on an empty line, or a line with a comment, the game will pause at the next valid line.
Now that we have a breakpoint set up, if we run the game using the debugger, the application will automatically stop right before that line of code is executed. So, for example, if you stop at a line where we assign a value to a variable, the variable itself won’t have that value yet, but it will change as soon as we step into the next line. Breakpoints can be set or removed at any point, even while the game is running.
While the game is paused, you can inspect the values of variables in a few different ways. The easiest one is to hover over the variable name in code. Know that the values of variables above the current line have already been updated for this frame while the ones below haven't. This also means that if you have a local variable, they won’t be initialized yet.
Checking values this way is useful for variable types such as integers or strings, but the information you get from data structures or objects isn’t too useful. In those cases, it’s better to use the Variables and Instances debug windows to get more information.
I have mentioned that you can run through your code line by line after the game is paused, either manually or by a breakpoint, but haven’t explained how to do it yet. If you look at the debug bar, you will notice these icons:
These are your helpers while navigating code. From left to right, these buttons are: Step into, Step over, and Step out.
Step into
Lastly, this button will bring you one step above in the call stack. If you are not sure what the call stack, it is the list of function calls that were performed to reach the part of the code you are in. So, for example, if you are inside of your object create event, and then you call a script that calls another script inside of itself, then you will be three functions deep. If you press the Step Out button now, you will be taken to the previous step in the stack (the first script call). If you press it once more, you will end up back at the object create event. Useful when you are inside of a script that you know is working as expected and want to get back to debugging the section you started on.
Another good way to navigate your code if you want to skip large sections of it, is to place another breakpoint where you want to reach and then press the Play button. This will make the game continue executing until it reaches the next breakpoint which should be the one you just placed.
As explained before, you can also press the Pause button to stop the execution of the game, but this will stop wherever the game was currently executing, so it may be a random location. You can also restart the game by pressing the circular arrow next to the Play/Pause buttons.
Alejandro Hitti is a videogame Programmer and Designer from Venezuela. Although his background is in C++ and working using custom-made game engines, his two commercial games, INK and HackyZack, were made using GameMaker Studio 1.4. With the release of GameMaker Studio 2, that became his engine of choice. The novelty of GMS2, paired with his knowledge of the previous version, ignited his interest to create tutorials that focus on this new engine.