It might come as a surprise, but not all code works on the first try. (Insert surprised copyright-safe yellow electrical rodent here)
For this reason, most integrated development environments (IDEs) include a debugger that allows the developer to spot and solve issues during the several development iterations. This is illustrated by the following pseudocode:
while developing == true:
code
test
debug
When developing a game, however, there are other issues to think about, such as game balance or verifying behaviors that might be too fast or subtle to notice during gameplay.
Think about it. You carefully crafted your game’s challenge level, so you want to make sure it’s up to the challenge. But how can you count the number of enemies in a game when they’re moving around and shooting at you?
You might want to check the game balance. Is the number of enemies spawned at a given instant what you expect? Did the power-up add the damage boost you expected?
These are just a few of the questions you might want to answer while testing your game. This is where Godot’s debug tools come in handy, helping you test and verify all that.
Getting Started
The example project is the same from the Publishing Your Godot Project to itch.io tutorial. However, for this article, the code was changed to enhance the gameplay experience. Unfortunately, the game broke in the process and you need to find what’s wrong with it using the debug tools. How convenient! :]
You can download the project using the Download materials links at the top and bottom of this article. Note that this project requires Godot 4.3 or newer to run.
The starting project has the same structure as in the previous article, but three files have changed: projectile.gd, enemy_ship.gd, and main_game.gd.
Now is a good time to download and run the project to review how it’s working and notice how the bugs influence the game. The main issue you’ll notice is that it’s impossible to destroy the enemy ships, although they can destroy your ship, so it’s debugging time!
Overview of the Debug Tools
Godot has a set of powerful debug tools you can use to review code for logic errors, or graphics for performance issues, or even to get an x-ray of how the game is using its processing time.
Although there’s a Debug menu at the top of the screen, this article will focus on the tools accessible through the Debugger panel at the bottom of the screen, because these are the tools that gather information when executing the project through the Run the project button.
Debugger Panel
The debugger panel, located at the bottom of the screen by default, is accessible from the Debugger option at the bottom of the window, to the right of the Output option. The following picture shows the debug panel:
At the panel’s top, you can see a few tabs. From left to right, the tabs are:
- Stack trace: Shows the execution stack, the context and its variables, and allows for controlling how the game executes during the debug session. You’ll learn more about this tab later in this article.
- Errors: Shows the error and warning messages during the game execution.
- Profiler: Shows which code is running and how it affects the game performance. You’ll learn more about this tab later in this article.
- Visual profiler: Displays a graph showing which code is running and how much time it takes for execution. You’ll learn more about this tab later in this article.
- Monitors: Contains graphs of game information, such as frames per second (fps), memory usage, scene nodes, and more. The data from the debug session is saved even after the session is over, so it’s possible to review it even after execution.
- Video RAM: Shows a list of resources and how much video RAM they use while running, as well as the grand total at the top of the panel.
- Misc: Monitors and identifies the control nodes clicked during runtime.
- Network Profiler: Contains the list of all nodes communicating over Godot’s multiplayer API and how much data each one of them received or sent during the debug session.
This article focuses on tabs 1, 2, 3, 4 and 5. However, feel free to examine the others using the same project. Some of them, such as Network Profiler won’t have any interesting information, though, as the game doesn’t use the multiplayer API in any point.
Using Breakpoints
After executing the game, you should have noticed that the main issue is that it’s impossible to destroy the enemy ships. So, logically, there must be a problem with the function invoked when damaging the enemy ships — maybe the ships don’t take damage when they should?
To test if this is true, examine the function that deals damage to the enemies: open projectile.gd and find the damage_body function. The function code is:
func damage_body(body: Node2D) -> void:
# 1
body.take_damage(3)
# 2
create_explosion()
# 3
queue_free()
This code does the following:
- When the bullet collides with the enemy ship, it reduces the ship’s health by 3 points;
- An explosion appears on the point where the collision happened;
- The bullet is removed from memory;
This is a straightforward function, and its logic seems to be correct. How can it not be working? Wouldn’t it be great if there was a way of getting a deeper look at how the code is working? That is where breakpoints come in, allowing you to halt the code execution and dig deeper to discover the problem.
Breakpoints
When analyzing code, the error might not be obvious just by looking at the code; you might want to have a look at the code during runtime. To do just that, you’ll need to use breakpoints and watches, which work together to assist and verify what the code is doing.
When you define a breakpoint, Godot knows it will need to execute the project normally up to that point. After that, it halts execution and allows you to control how fast the code should execute, and lets you see which code executes in case of conditionals or loops.
The description should be kind of abstract now, but you’ll see that in practice it’s very simple and handy!