AgentSkillsCN

godot-debugging-profiling

专家级调试流程,包括打印调试(push_warning、push_error、assert)、断点(条件断点)、Godot 调试器(堆栈跟踪、变量、远程调试)、性能分析器(时间分析器、内存监控)、错误处理模式,以及性能优化。适用于修复 Bug、调优性能,或进行开发诊断。触发关键词:断点、print_debug、push_error、assert、profiler、远程调试、memory_leak、orphan_nodes、Performance.get_monitor。

SKILL.md
--- frontmatter
name: godot-debugging-profiling
description: "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."

Debugging & Profiling

Expert guidance for finding and fixing bugs efficiently with Godot's debugging tools.

NEVER Do

  • NEVER use print() without descriptive contextprint(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

gdscript
# 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:
gdscript
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:

  1. Run project (F5)
  2. Debug → Remote Debug → Select running instance
  3. Inspect live game state

Common Debugging Patterns

Null Reference

gdscript
# ❌ Crash: null reference
$NonExistentNode.do_thing()

# ✅ Safe: check first
var node := get_node_or_null("MaybeExists")
if node:
    node.do_thing()

Track State Changes

gdscript
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

gdscript
func _draw() -> void:
    if Engine.is_editor_hint():
        draw_line(Vector2.ZERO, ray_direction * ray_length, Color.RED, 2.0)

Debug Draw in 3D

gdscript
# 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

gdscript
# 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

gdscript
# Check in _process
func _process(delta: float) -> void:
    print(Engine.get_frames_per_second())  # Monitor FPS

Issue: Memory Leaks

gdscript
# 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

gdscript
# Check for orphans
func check_orphans() -> void:
    print("Orphan nodes: ", Performance.get_monitor(Performance.OBJECT_ORPHAN_NODE_COUNT))

Debug Console

gdscript
# 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

gdscript
const DEBUG := true

func debug_log(message: String) -> void:
    if DEBUG:
        print("[DEBUG] ", message)

2. Conditional Breakpoints

gdscript
# Only break on specific condition
if player.health <= 0:
    breakpoint

3. Scene Tree Inspector

code
Debug → Remote Debug → Inspect scene tree
See live node hierarchy

Reference

Related