Debugging Projects in Godot
When analyzing code, the errors might not be obvious just by looking at the code. To help you analyze and debug code, use the provided debugger integrated in Godot. By Ricardo Santos.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Debugging Projects in Godot
25 mins
- Getting Started
- Overview of the Debug Tools
- Debugger Panel
- Using Breakpoints
- Breakpoints
- Adding Breakpoints to the Project
- Using the Debugger Panel
- Stack Trace Tab
- Errors Tab
- Using the Profiler
- Profiler Information
- Using the Profiler to Optimize the Game
- Visual Profiler Tab
- Custom Monitors
- Creating Custom Performance Monitors
- Using Custom Performance Monitors
- Where to Go From Here?
Adding Breakpoints to the Project
The first assumption is that the body.take_damage() function is not working properly. Move the mouse cursor to the left of the line until a red dot appears. Left-click to set the breakpoint on the line, which appears as a bright red dot, and you’re set to begin the detective work to discover how to solve the issue with the project.
This sets the breakpoint on line 32 and requires the execution to stop when it reaches this line. In addition to that, notice that the Debug panel has now updated the list of breakpoints in the project on the right side.
This list is particularly useful when you need to verify where all the project breakpoints are and to see if any of them are no longer relevant to what you are debugging. For instance, when you’re finished debugging one part of the game and want to move on to another one, it’s good to clear the old breakpoints to give way to the new ones.
Click the Run the project button to launch the game, begin playing it and notice that when a bullet hits a target, the editor comes to the foreground with a yellow arrow marking where it stopped.
The yellow arrow always indicates which line the execution is at. Now, it’s stopped in one line, but you’ll notice that in the next section it will change to show other parts of the code.
Using the Debugger Panel
You already set the breakpoint, but you couldn’t get any new information from the code yet. You need more insight into what’s going on in your code, how your variables are used, and how their values change over time. Not only that, but you also need ways to control how the code executes.
These are the main functionalities you’ll find in the Stack trace tab, each waiting to show you something new about your code.
Stack Trace Tab
The leftmost tab in the Debug panel is called Stack Trace and is the one invoked automatically when execution halts. When the bullet hits the other ship, execution should stop and the Stack Trace panel should appear at the bottom of the screen.
This panel has four main elements:
- The processing stack: Lists the function stack trace to the debugged code.
- Stack Variables: Lists the variables visible in the current context.
- Breakpoints: Lists the breakpoints in the project.
- Execution control buttons: Controls the program’s execution.
Turn your attention back to the paused game and the code on screen. The main assumption here is that the enemies’ health is not decreasing as it should. To verify that, click the Step Into button (or press F11 on your keyboard).
Godot should take you into the take_damage() function, which reads:
func take_damage(amount: int) -> void:
# 1
if health >= 0:
# 2
health -= amount
# 3
if health == 0:
# 4
die()
else:
# 5
flash_red()
Line by line, this code:
- Tests whether the ship’s health is greater or equal to zero.
- Receives damage and decreases health by the damage taken.
- Tests whether health equals zero.
- If it is, the ship should die.
- If not, flash red to demonstrate damage was sustained.
This logic seems sound as well. Click the Step Over button to go to the next line and look at the variables below.
Note that the amount variable has a value of 3, and the current enemy health is 2, so if the damage mechanism is working well, the enemy ship should be destroyed. Click Step over two more times to see how the variables progress.
Notice how the value of your health variable becomes -1. This means that the enemy ship receives the damage and should be destroyed. Click Step over once more to see how the conditional statement executes and where the bug is.
You’ll notice that Godot executes flash_red() function, instead of the expected die() function. The error is in the conditional statement.
A subtle error, the code expects the health variable to be equal to zero, but it is less than zero.
Stop the game and correct the if statement like the following:
func take_damage(amount: int) -> void:
if health >= 0:
health -= amount
if health <= 0:
die()
else:
flash_red()
Now, run the game and test again to verify that the enemies get destroyed. This time around, you might feel that the breakpoint is just getting in your way instead of helping. Of course, you could remove the breakpoint and run the game normally.
Or, and hear me out, you could tell Godot to ignore all the breakpoints. You can do that by toggling the Skip breakpoint button and running the game. In this case, when there’s only one breakpoint, this might seem like overkill. However, in a long debug session, with several breakpoints in the project, this might be very welcome as an alternative to disabling every breakpoint in the project.
This time around, the game got really slow and feels so choppy. What might be causing that? Stop the game and examine the Debugger panel a little more.
Turn your attention to the next tab in the Debugger panel: the Errors tab. You’ll notice a lot of action there. Click the tab to see what it has to say.
Errors Tab
By now, you should have something that looks like the figure below:
Wow, more than 1,000 messages! This surely is causing the slowness. Scroll past the warnings until you get to the error messages, and you should see the error messages shown above.
These messages were issued by the main_game.gd script. Click one of these messages to go to the code line that’s generating this message.
func _process(delta: float) -> void:
if score == 0:
return
# Idea for Steam: If the player score is in the Fibonacci sequence, give a trophy.
for n in sequenceNumbers.size():
if score == sequenceNumbers[n]:
push_warning("In your score history you have a number that is in the Fibonacci sequence of the scores you have had.")
else:
push_error("In your score history you do not have a number that is in the Fibonacci sequence of the scores you have had.")
sequenceNumbers.append(score)
for n in 2000:
sequenceNumbers.append(sequenceNumbers[n-1]+sequenceNumbers[n])
It seems that this is some fill-in code for a Steam trophy. To be honest, there are worse trophies out there. So it would be good to know if the impact in the game execution time is worth it.
The tools already covered do not give that kind of information, so you need something else: Enter the Profiler tools.