"Expert debugging workflows including print debugging (push_warning, push_error, assert), breakpoints (conditional breakpoints), Godot Debugger (stack trace, variables, remote debug), profiler (time profiler, memory monitor), error handling patterns, and performance optimization. Use for bug fixing, performance tuning, or development diagnostics. Trigger keywords: breakpoint, print_debug, push_error, assert, profiler, remote_debug, memory_leak, orphan_nodes, Performance.get_monitor."
Resources
1Install
npx skillscat add thedivergentai/gd-agentic-skills/godot-debugging-profiling Install via the SkillsCat registry.
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. Useprint("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. Useif not condition: push_error()for runtime checks. - NEVER profile in debug mode — Debug builds are 5-10x slower. Always profile with release exports or
--releaseflag.
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
breakpointkeyword:
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 = valueVisualize 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.dataProfiler
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 FPSIssue: 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_visibleBest 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:
breakpoint3. Scene Tree Inspector
Debug → Remote Debug → Inspect scene tree
See live node hierarchyReference
Related
- Master Skill: godot-master