In my last post, I explained my process when debugging games and some tips to find bugs quickly and identify bottlenecks in your game. In this post, I'll cover some more advanced debugging techniques and also talk about profiling your game.
Debugging draw events and surfaces
These are probably my favorite set of tools, since I work with surfaces and draw code a lot. I’m talking specifically about the Render States and Surfaces & Textures windows. In render states, you have all the different variables that determine how each object is changed before being drawn. Each of these variables has a function associated with it to change their values, so I will not cover those, but it’s good to know you can check the current state of all of them if you need to.
We then have the Surfaces & Textures window, which is divided in two sections, one for each. When you have a GameMaker project, your sprites are merged and put into what is called Texture Pages. The reason why GameMaker does this is to avoid sending hundreds of images to the GPU, one for each sprite, or even one for each frame of each animation of the sprite.
Instead, GM sends the GPU a big texture page and the coordinates of each sprite you want to draw. This makes the process a lot faster if used correctly. What I mean is that it is in your best interest to get into the habit of grouping sprites you know will appear together (such as enemies and tilesets from a specific level or world) into their own texture group. When you do this, GameMaker will do its best to group those sprites together into the same texture page, which makes it so we don’t have to send too many textures to the GPU and the game runs faster.
In my example below, I didn’t do this, which is why you can see a mess of sprites that don’t belong together in each page. It also created huge loading times for me, as GameMaker has to create all texture pages in the texture group when you change as single sprite in it, which was default in my case for every single one. Don’t make the same mistakes I did.
If you are in the Textures tab, you can see all your texture pages. If they are not showing up, just click on the refresh icon and they should appear. Then, you can hover over any of the images to see an enlarged version and check that the sprites you want grouped together are in the same page.
The second part to this window is the Surface one. It works the same way as the Texture section, but instead it shows all the current surfaces, including the application surface (where everything is drawn to). The refresh button is a lot more important in this section, as surfaces can change between frames, so always to remember to refresh so you know for sure you are looking at the latest version of each surface.
Because surfaces are created at run-time, sometimes it’s hard to know if what you think should be drawn is actually what’s being drawn. With this tool, you can easily check that the surfaces created have the correct size and information drawn on it, and even debug it when you are composing multiple surfaces and shaders together to create an effect, like I did with HackyZack’s book menu.
Below you can see an example of all the different surfaces that had to be created to draw the book and all the stickers, player, cursor, etc.
Miscellaneous debug windows
I’ve covered most of the tools provided by the debugger, but a few don’t fall into any previous category, so I will quickly mention them now. The first is the Call Stack window. Previously I talked about the call stack, and this is a visual representation of it. The cool feature is that you can double-click on any entry in the stack and it will take you to the part of the code where this specific script was called from. This is especially useful when trying to figure out which object or event called a script used in multiple places throughout your code.
Another cool little window you can display with the debugger is the Graph. This handy tool shows you important performance information about your game, such as the current and average FPS and Memory values. You may also notice a few vertical lines in the graph, the white ones being events called by the user (using the functions show_debug_message() and debug_event), and the blue ones being system events. If you hover on these lines, you will see more detailed information about what happened at that point, which is useful if you notice that the line aligns perfectly with a memory spike or an FPS drop.
The last window I wanted to mention is for Buffers. I will admit that I’ve never used buffers in GameMaker, so I don’t have a good example of what it looks like nor how useful it can be. However, I know it is essentially a hex editor for a buffer, which you select by specifying the id. You can also select the alignment and how the data is displayed by clicking on the burger icon at the bottom right, or by right clicking anywhere on the window.
That is all for the debugger portion of this article! We have one more topic to cover before we are done, which is profiling your game.
Using the profiler
In the Other tab, you will find the Profiler window. This tiny box has a lot of information and functionality to go through, so let’s get started. To profile your game, all you need to do is start the game with the debugger attached (F6) and get to the section of the game you want to test. It is advised to test sections separately as you will gather a ton of data and it’s easier to understand what’s going on if you approach it a chunk at a time. For example, you may want to test your menus first, then your normal levels, then a specific boss fight. This separates the sets of data, making it easier to digest and analyze. When we are ready, all we have to do is press Start Profiling and GameMaker will collect data. Likewise, when you feel you have collected enough data, you can press Stop Profiling and the information will stay intact for you to parse through.
As you may notice, there is a ton of information being provided, which is good. With the current settings, the profiler will list each event being run by every instance in the room. Next, it shows three values, which are the average number of times that event is called each frame, the time (in Ms) it takes to execute that event, and the percentage of the frame that event takes. We can also get the total value of these three values instead of the average, by just unchecking the Average Values box.
You may have noticed that each event also has a plus sign next to it to show more information. If we open it up, it will give us a more detailed look at every function call performed inside of that event, providing the same three values broken down into smaller pieces.
Now, if this mode is useful to determine if a specific instance or event is taking up too much time in the frame, but what if we want to know how much time is being used up by a script or function? We can then change the view mode from
Bottom-Up. In this mode, we will see each function and script displayed, and the information that can be expanded will be the specific event that is calling that function.
The last dropdown we can interact with determines if we want to profile only calls made in our code, the engine calls, or both at the same time. If we select engine, we will see how long it takes to do a step, how long we spending drawing the room, and information of the sort. In the combined view, we see similar information, and to access our calls, we can expand the individual events (HandleStep, HandleAlarm, DrawTheRoom, for example) to see it.
Finally, a note I wanted to add is that GameMaker, by default, uses a framerate controller that automatically stops the execution of the game when it finished doing all its operations, capping the framerate to what we set the room speed to be. Typically, games run at 30 FPS or 60 FPS, so if we do some math, we conclude that a game running at 30 FPS needs to finish every frame in
one second / 30 frames, which is
33 Ms. For a 60 FPS game, every frame has to finish in
16 Ms or less. If your game is running at a high framerate in the background, the
Finish_Frame event in the Engine/Combined section will usually be a big chunk of each of your frames (32.5 Ms in my example).
The topic of debugging and profiling is extensive and can be confusing if you are new to it. I hope you try it and take advantage of this tool moving forward when debugging and optimizing your game as it is invaluable. And, I can’t stress enough how useful and well written the official GameMaker documentation is. I definitely recommend that you open it (F1 while in GameMaker), or by going to this link: GameMaker 2 Documentation and then clicking on Extras → Debugging (or clicking HERE to get there directly).
I am always willing to help if you run into any issues with the material covered in the article, or something else related to GMS2. You can contact me on
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.