- Debugging & Profiling
- Expert guidance for finding and fixing bugs efficiently with Godot's debugging tools.
- NEVER Do
- NEVER use
- print()
- without descriptive context
- —
- print(value)
- is useless. Use
- print("Player health:", health)
- with labels.
- NEVER leave debug prints in release builds
- — Wrap in
- if OS.is_debug_build()
- or use custom DEBUG const. Prints slow down release.
- NEVER ignore
- push_warning()
- messages
- — Warnings indicate potential bugs (null refs, deprecated APIs). Fix them before they become errors.
- NEVER use
- assert()
- for runtime validation in release
- — Asserts are disabled in release builds. Use
- if not condition: push_error()
- for runtime checks.
- NEVER profile in debug mode
- — Debug builds are 5-10x slower. Always profile with release exports or
- --release
- flag.
- Available Scripts
- MANDATORY
- Read the appropriate script before implementing the corresponding pattern. performance_plotter.gd Custom Performance monitors for gameplay metrics (projectile/enemy count). Includes automated error state capture with stack traces and memory stats. debug_overlay.gd In-game debug UI with real-time FPS, memory, orphan nodes, and custom metrics. Do NOT Load debug_overlay.gd in release builds - wrap usage in if OS.is_debug_build() . Print Debugging
Basic print
print ( "Value: " , some_value )
Formatted print
print ( "Player at %s with health %d" % [ position , health ] )
Print with caller info
print_debug ( "Debug info here" )
Warning (non-fatal)
push_warning ( "This might be a problem" )
Error (non-fatal)
push_error ( "Something went wrong!" )
Assert (fatal in debug)
assert ( health
0 , "Health cannot be negative!" ) Breakpoints Set Breakpoint: Click line number gutter in script editor Or use breakpoint keyword: func suspicious_function ( ) -> void : breakpoint
Execution stops here
- var
- result
- :=
- calculate_something
- (
- )
- Debugger Panel
- Debug → Debugger
- (Ctrl+Shift+D)
- Tabs:
- Stack Trace
-
- Call stack when paused
- Variables
-
- Inspect local/member variables
- Breakpoints
-
- Manage all breakpoints
- Errors
- Runtime errors and warnings Remote Debug Debug running game: Run project (F5) Debug → Remote Debug → Select running instance Inspect live game state Common Debugging Patterns Null Reference
❌ Crash: null reference
$NonExistentNode . do_thing ( )
✅ Safe: check first
var node := get_node_or_null ( "MaybeExists" ) if node : node . do_thing ( ) Track State Changes var _health : int = 100 var health : int : get : return _health set ( value ) : print ( "Health changed: %d → %d" % [ _health , value ] ) print_stack ( )
Show who changed it
_health
value Visualize Raycasts func _draw ( ) -> void : if Engine . is_editor_hint ( ) : draw_line ( Vector2 . ZERO , ray_direction * ray_length , Color . RED , 2.0 ) Debug Draw in 3D
Use DebugDraw addon or create debug meshes
func debug_draw_sphere ( pos : Vector3 , radius : float ) -> void : var mesh := SphereMesh . new ( ) mesh . radius = radius var instance := MeshInstance3D . new ( ) instance . mesh = mesh instance . global_position = pos add_child ( instance ) Error Handling
Handle file errors
func load_save ( ) -> Dictionary : if not FileAccess . file_exists ( SAVE_PATH ) : push_warning ( "No save file found" ) return { } var file := FileAccess . open ( SAVE_PATH , FileAccess . READ ) if file == null : push_error ( "Failed to open save: %s" % FileAccess . get_open_error ( ) ) return { } var json := JSON . new ( ) var error := json . parse ( file . get_as_text ( ) ) if error != OK : push_error ( "JSON parse error: %s" % json . get_error_message ( ) ) return { } return json . data Profiler Debug → Profiler (F3) Time Profiler Shows function execution times Identify slow functions Target: < 16.67ms per frame (60 FPS) Monitor FPS, physics, memory Object count Draw calls Common Performance Issues Issue: Low FPS
Check in _process
func _process ( delta : float ) -> void : print ( Engine . get_frames_per_second ( ) )
Monitor FPS
Issue: Memory Leaks
Check with print
func _exit_tree ( ) -> void : print ( "Node freed: " , name )
Use groups to track
add_to_group ( "tracked" ) print ( "Active objects: " , get_tree ( ) . get_nodes_in_group ( "tracked" ) . size ( ) ) Issue: Orphaned Nodes
Check for orphans
func check_orphans ( ) -> void : print ( "Orphan nodes: " , Performance . get_monitor ( Performance . OBJECT_ORPHAN_NODE_COUNT ) ) Debug Console
Runtime debug console
var console_visible := false func _input ( event : InputEvent ) -> void : if event is InputEventKey and event . keycode == KEY_QUOTELEFT : console_visible = not console_visible $DebugConsole . visible = console_visible Best Practices 1. Use Debug Flags const DEBUG := true func debug_log ( message : String ) -> void : if DEBUG : print ( "[DEBUG] " , message ) 2. Conditional Breakpoints
Only break on specific condition
if player . health <= 0 : breakpoint 3. Scene Tree Inspector Debug → Remote Debug → Inspect scene tree See live node hierarchy Reference Godot Docs: Debugger Related Master Skill: godot-master