mcpField
G
godot-debugging

godot-debugging

github.com/jame581开发901197 次安装1 周前更新
代码仓库

关于 godot-debugging

面向 Godot 4.3+ GDScript 和 C# 项目的系统化调试技能,讲解日志、断点、信号、场景树和性能分析,并提供逐步排错流程。

主要功能

  1. 01使用日志与断点检查执行状态
  2. 02检查信号连接和运行时节点
  3. 03使用内置分析器定位性能瓶颈
  4. 04识别常见错误及其原因
  5. 05按步骤复现、修复并验证问题

适用场景

  1. 01排查节点路径或空实例错误
  2. 02调查信号重复触发或未连接
  3. 03定位函数耗时与内存增长
  4. 04修复问题后添加回归测试

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jame581/godotprompter --skill godot-debugging

安装后在 Claude Code 中输入/godot-debugging确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
performance-debugging.md
scene-tree-debugging.md
signal-tracing.md
systematic-method.md

详细介绍

Godot 调试

本技能介绍如何对 Godot 4.3+ 项目进行系统化调试,涵盖 GDScript 和 C#。内容包括打印技巧、断点、信号追踪、内置分析器、场景树检查、常见错误模式,以及分步调试清单。

相关技能: 性能分析请参阅 godot-optimization,修复后的回归测试请参阅 godot-testing,C# 信号调试模式请参阅 csharp-signals。


1. 打印调试

GDScript

Godot 提供了多种用途各异的打印函数。请根据日志的严重程度和上下文选择合适的函数。

# print() — general output, space-separated values
print("Player position: ", position)
print("Health: ", health, " / ", max_health)

# print_rich() — BBCode-formatted output in the Output panel
print_rich("[color=yellow]WARNING:[/color] Enemy count exceeded limit: ", enemy_count)
print_rich("[b]State:[/b] [color=green]", current_state, "[/color]")

# push_error() — logs an error with a full stack trace; does NOT stop execution
push_error("save_game: file path is empty")

# push_warning() — logs a warning with stack trace; use for recoverable issues
push_warning("AudioStreamPlayer: bus '%s' not found, using Master" % bus_name)

# print_debug() — print() plus the calling file:line when the debugger is attached;
# it still prints in release exports (guard with OS.is_debug_build() to silence it)
print_debug("Frame delta: ", delta, " | FPS: ", Engine.get_frames_per_second())

# printerr() — prints to stderr; visible in external terminals and CI logs
printerr("Critical: physics state corrupted at frame ", Engine.get_process_frames())

格式化输出模式:

# String formatting with % operator
print("Actor [%s] dealt %d damage to [%s]" % [name, damage, target.name])

# String.format() with named placeholders
var msg := "Position: ({x}, {y}) at speed {spd}"
print(msg.format({"x": position.x, "y": position.y, "spd": velocity.length()}))

# Printing arrays and dictionaries — use str() for clean output
var inventory := {"sword": 1, "potion": 3}
print("Inventory: ", str(inventory))

# Conditional verbose logging using a project-level constant or autoload flag
if DebugConfig.verbose_ai:
    print_rich("[color=cyan][AI][/color] ", agent.name, " chose action: ", chosen_action)
// String interpolation
GD.Print($"Actor [{Name}] dealt {damage} damage to [{target.Name}]");

// Printing collections
var inventory = new Godot.Collections.Dictionary { { "sword", 1 }, { "potion", 3 } };
GD.Print("Inventory: ", inventory);

// Conditional verbose logging
if (DebugConfig.VerboseAi)
    GD.PrintRich($"[color=cyan][AI][/color] {agent.Name} chose action: {chosenAction}");

各函数的适用场景:

函数Release 中可见堆栈跟踪用途
print()是(如果未移除)否检查一般值
print_rich()是否输出分类、带颜色的日志
push_error()是是无效状态、程序员错误
push_warning()是是可恢复的问题
print_debug()是调试器已连接时显示调用行查找打印某个值的位置
printerr()是否输出到外部终端 / CI
C#
using Godot;

public partial class Player : CharacterBody3D
{
    public override void _Ready()
    {
        // GD.Print — equivalent to GDScript print()
        GD.Print("Player position: ", Position);

        // GD.PrintRich — BBCode formatted
        GD.PrintRich("[color=yellow]Ready called on[/color] ", Name);

        // GD.PushError — logs error with stack trace
        GD.PushError("_Ready: required child node missing");

        // GD.PushWarning — logs warning with stack trace
        GD.PushWarning("AudioBus not found, falling back to Master");

        // GD.PrintErr — writes to stderr
        GD.PrintErr("Critical failure in _Ready");
    }

    private void HandleDamage(int amount)
    {
        // Formatted string output
        GD.Print($"[{Name}] took {amount} damage. HP: {_health}/{_maxHealth}");
    }
}

2. 断点和远程调试器

设置断点
  • 在脚本编辑器中,点击代码行号左侧的边栏即可切换断点。出现红点表示已设置断点。
  • 使用 F9 切换当前行的断点。
  • 在 GDScript 中使用 breakpoint 语句可触发程序化断点:
func _physics_process(delta: float) -> void:
    if velocity.length() > MAX_SPEED:
        breakpoint  # execution pauses here during debug runs
    move_and_slide()
  • 在 C# 中,使用 System.Diagnostics.Debugger.Break(),或连接 .NET 调试器(例如 JetBrains Rider,或安装 Godot 扩展的 VS Code)。
public override void _PhysicsProcess(double delta)
{
    if (Velocity.Length() > MaxSpeed)
    {
        System.Diagnostics.Debugger.Break(); // pause if .NET debugger is attached
    }
    MoveAndSlide();
}
使用内置调试器面板

执行在断点处暂停时,编辑器底部的 Debugger 面板提供以下功能:

  • Stack Frames — 显示完整调用堆栈;点击某个堆栈帧可检查其局部变量。
  • Locals / Members / Globals — 实时检查和修改变量值。
  • Step Into (F11) / Step Over (F10) — 逐行执行(Godot 4.6 起支持 Step Out,快捷键为 Alt+F11)。
  • Continue (F12) — 恢复执行,直到遇到下一个断点。
远程场景检查器

游戏运行期间暂停或处于会话中时:

  1. 在编辑器中打开 Debugger > Remote 选项卡。
  2. 点击 Scene 面板中的 Remote(左上角,「Scene」旁的切换项),将场景树切换为实时视图。
  3. 点击任意实时节点,即可在 Inspector 中检查其当前属性。
  4. 在此处修改的属性会立即生效,可用于测试。
Monitors 选项卡

Debugger > Monitors 显示实时引擎指标:

  • FPS / Process time / Physics time — 发现性能回退。
  • Video RAM / Object count / Node count — 跟踪内存增长。
  • Physics 2D/3D collision pairs — 找出开销较大的物理场景。
  • Audio latency — 发现音频回调超时。

点击任意监视器名称可打开对应图表。使用 Add 按钮创建自定义监视器仪表板。


3. 信号调试

运行时可通过 node.get_signal_connection_list("signal_name") 检查(返回包含 callable、flags、signal 的 Dictionary 数组)。最常见的信号错误包括:重复连接(处理程序触发两次)、释放时忘记断开连接(产生警告和悬空引用)、处理程序签名错误(每次发出信号时都会出现 Error calling from signal …)。

完整的 GDScript 和 C# 检查辅助工具,以及常见信号问题目录(重复连接、延迟释放竞态、lambda 捕获的生命周期、信号与 Callable 的选择),请参阅 references/signal-tracing.md。


4. 常见错误模式

错误消息原因修复方法
Node not found: "Player" (relative to "...")节点路径错误、节点已重命名,或在节点添加到场景树之前访问仅在 _ready() 中或之后使用 $NodeName。使用 print(get_node_or_null("Player")) 验证路径。使用 @onready。
Attempt to call function on a null instance节点已释放、未赋值导出变量,或 get_node() 返回了 null使用 is_instance_valid(node) 进行保护。在 Inspector 中检查导出变量。优先使用 @onready var _node := $Node。
Can't change this state while flushing queries在 body_entered 等物理回调中修改物理状态(例如禁用 CollisionShape)延迟更改:collision_shape.set_deferred("disabled", true)。
Invalid call. Nonexistent function 'X' in base 'Y'调用了该类型不存在的方法,或以错误类型访问节点检查 class_name,使用 as 进行类型转换,或确认脚本已附加。使用 has_method("X") 进行保护。
Converting circular structure to JSON. / Circular reference to resource being saved foundResource 或 Dictionary 直接或间接引用了自身打破循环。尽可能使用节点引用代替资源引用,或将子资源标记为仅本地。
Cannot call non-static function "x()" from the static function "y()".在 static func 中调用实例方法将调用移到实例上下文中,或重构为接收数据作为参数的静态辅助函数。
Stack overflow. Check for infinite recursion in your script.无限递归——通常是信号触发自身,或 setter 为自身赋值在 setter 中添加保护变量(_updating := true)。在 Debugger 中追踪调用栈。
Signal 'x' is already connected to given callable '…' in that object.未使用 CONNECT_REFERENCE_COUNTED,却对同一信号/可调用对象组合再次调用 connect()连接前检查 is_connected(),或先断开连接,或使用 CONNECT_REFERENCE_COUNTED。
Out of bounds get index 'X' (on base: 'Array')访问 Array 或 PackedArray 时索引超出其长度访问前验证索引:if index < array.size()。在适用时使用 array.get(index)。
音频出现噼啪声或中断(缓冲区欠载)音频回调未能按时完成;音频线程处理任务过多减少音频总线效果、降低复音数,或在 Project Settings 中增大音频缓冲区。

5. 性能调试

Profiler(Debugger → Profiler)会显示各函数的自身耗时和调用次数。Monitors 标签页会跟踪帧时间、FPS、绘制调用数、物理帧预算和内存。在负载最高的游戏场景中同时打开两者;按 Self time 对 Profiler 排序以找出问题所在。对于绘制调用瓶颈,请在 Monitors 标签页中查看 Raster > Total Draw Calls。

有关 Profiler 工作流、Monitors 标签页用法、绘制调用瓶颈识别和物理帧监控模式,请参阅 references/performance-debugging.md。识别出瓶颈后,也请参阅 godot-optimization 了解修复方法。


6. 场景树调试

print_tree_pretty() 会将当前场景树转储到 stdout,这是确认节点是否位于预期位置的最快方法。游戏运行时,编辑器中的 Remote 标签页会显示实时场景树。对于 @tool 节点,请实现 _get_configuration_warnings(),以便在编辑器的 SceneTree 面板中显示配置错误。

有关完整示例(print_tree_pretty 模式、节点组调试辅助函数、_get_configuration_warnings 的 GDScript + C#),请参阅 references/scene-tree-debugging.md。


7. 系统化调试方法

当打印、断点和远程检查无法立即揭示 bug 时,请采用有步骤的方法:复现 → 隔离 → 提出假设 → 追踪 → 修复 → 验证 → 添加测试。 每一步都是进入下一步的前提;跳步会浪费时间。

查看 references/systematic-method.md,了解包含各步骤具体技巧(如二分查找、最小复现、回归测试模式)的完整 7 步方法。


8. 实施检查清单

  • 使用 OS.is_debug_build() 限制详细的帧级输出——print() 和 print_debug() 都不会从发布版本中移除
  • 对无效状态和可恢复的问题,使用 push_error() / push_warning()(而非 print())——它们会包含堆栈跟踪
  • 使用 F9 或 breakpoint 语句设置断点以暂停执行,而不是到处插入打印语句
  • 在认定信号连接正确之前,先使用 get_signal_connection_list() 检查信号连接
  • 调试期间打开 Scene → Remote 检查实时场景树,确认运行时节点状态
  • 打开 Debugger → Profiler 测量自身耗时,然后再优化——首先找出真正的瓶颈
  • 观察 Debugger → Monitors 中不断增长的 Object Count 或 Video RAM,以发现内存泄漏
  • 使用 is_instance_valid() 保护任何在 await 之后运行的代码,以防节点在等待期间被释放
  • 遵循复现 → 隔离 → 提出假设 → 追踪 → 修复 → 验证 → 测试的顺序;不要跳步直接修复
  • 修复 bug 后编写一个带名称的回归测试,避免同一故障悄然再次发生

评论

支持 Markdown