godot-debugging について
GDScript または C# を使う Godot 4.3+ プロジェクト向けの体系的なデバッグスキルです。ログ、ブレークポイント、シグナル、シーンの確認、プロファイリングと、修正を検証する手順を扱います。
主な機能
- 01ログとブレークポイントで実行状態を確認
- 02シグナル接続と実行中のノードを確認
- 03組み込みプロファイラーでボトルネックを特定
- 04よくあるエラーと原因を把握
- 05手順に沿って問題を再現・修正・検証
活用シーン
- 01ノードパスや null インスタンスのエラーを調査
- 02重複または未接続のシグナルを追跡
- 03関数の遅延やメモリ増加を特定
- 04バグ修正後に回帰テストを追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ jame581/ godotprompter --skill godot-debuggingインストール後、Claude Code で/godot-debuggingを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/performance-debugging.mdscene-tree-debugging.mdsignal-tracing.mdsystematic-method.md詳細
Godot のデバッグ
このスキルでは、GDScript と C# の両方を対象に、Godot 4.3 以降のプロジェクトを体系的にデバッグする方法を説明します。print を使ったテクニック、ブレークポイント、シグナルの追跡、組み込みプロファイラー、シーンツリーの検査、よくあるエラーパターン、手順に沿ったデバッグチェックリストを扱います。
関連スキル: パフォーマンスのプロファイリングについては godot-optimization、修正後の回帰テストについては godot-testing、C# のシグナルのデバッグパターンについては csharp-signals を参照してください。
1. print によるデバッグ
GDScript
Godot には、用途の異なる複数の print 関数が用意されています。ログの重要度と状況に応じて選択してください。
# 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}");
各関数の用途:
| 関数 | リリースビルドで表示 | スタックトレース | 用途 |
|---|---|---|---|
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 パネルで次の操作を行えます。
- スタックフレーム — 完全な呼び出しスタックを表示します。フレームをクリックすると、そのローカル変数を調べられます。
- ローカル/メンバー/グローバル — 変数の値を実行中に確認・変更できます。
- ステップイン(F11)/ステップオーバー(F10) — 行ごとに実行を進めます(Godot 4.6 では ステップアウト、Alt+F11 も使用できます)。
- 続行(F12) — 次のブレークポイントまで実行を再開します。
リモートシーンインスペクター
実行中のゲームが一時停止中、またはセッションの途中であるときは、次の手順を実行します。
- エディターで Debugger > Remote タブを開きます。
- シーンパネル(左上の「Scene」の横にある切り替えボタン)で Remote をクリックし、シーンツリーを実行中の表示に切り替えます。
- 実行中のノードをクリックすると、インスペクターで現在のプロパティを確認できます。
- ここで変更したプロパティは、テスト用にすぐに反映されます。
Monitors タブ
Debugger > Monitors では、エンジンのメトリクスをリアルタイムで表示できます。
- FPS/処理時間/物理演算時間 — パフォーマンスの低下を検出します。
- ビデオ RAM/オブジェクト数/ノード数 — メモリの増加を追跡します。
- 物理演算 2D/3D の衝突ペア数 — 処理負荷の高い物理シーンを特定します。
- オーディオレイテンシ — オーディオコールバックのオーバーランを検出します。
モニター名をクリックすると、そのグラフが開きます。Add ボタンを使うと、カスタムのモニターダッシュボードを作成できます。
3. シグナルのデバッグ
実行時に node.get_signal_connection_list("signal_name") で確認できます(callable、flags、signal を含む Dictionary の Array を返します)。よくあるシグナルの不具合は、接続を二重に行うこと(ハンドラーが 2 回呼び出される)、解放時に切断し忘れること(警告とダングリング参照が発生する)、ハンドラーのシグネチャが誤っていること(emit のたびに Error calling from signal … が発生する)です。
GDScript と C# の完全な検査ヘルパー、およびよくあるシグナルの問題の一覧(重複接続、deferred-free の競合、ラムダキャプチャのライフサイクル、signal と 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) でチェックします。インスペクターでエクスポート変数を確認します。@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 found | Resource または Dictionary が直接または間接的に自身を参照している | 循環参照を解消します。可能な場合はリソース参照の代わりにノード参照を使用するか、サブリソースをローカル専用としてマークします。 |
Cannot call non-static function "x()" from the static function "y()". | static func からインスタンスメソッドを呼び出している | 呼び出しをインスタンスのコンテキストに移すか、引数としてデータを受け取る適切な静的ヘルパーにリファクタリングします。 |
Stack overflow. Check for infinite recursion in your script. | 無限再帰です。多くの場合、シグナルが自身を再度トリガーしているか、セッターが自身を設定しています | セッターにガード変数(_updating := true)を追加します。デバッガーでコールスタックを追跡します。 |
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) を使用します。 |
| 音声にノイズが入る、または途切れる(アンダーラン) | 音声コールバックが期限に間に合わない。音声スレッドで処理しすぎている | オーディオバスのエフェクトを減らす、ポリフォニーを下げる、またはプロジェクト設定でオーディオバッファーサイズを大きくします。 |
5. パフォーマンスのデバッグ
プロファイラー(デバッガー → プロファイラー)には、関数ごとの自己実行時間と呼び出し回数が表示されます。モニタータブでは、フレーム時間、FPS、ドローコール数、物理ティックの予算、メモリを追跡できます。最も負荷の高いゲームプレイシナリオで両方を開き、プロファイラーを自己実行時間で並べ替えて、ボトルネックの原因を見つけます。ドローコールのボトルネックを調べるには、モニタータブの Raster > Total Draw Calls を確認します。
プロファイラーのワークフロー、モニタータブの使用方法、ドローコールのボトルネックの特定、物理ティックの監視パターンについては、references/performance-debugging.md を参照してください。ボトルネックを特定した後の修正方法については、godot-optimization も参照してください。
6. シーンツリーのデバッグ
print_tree_pretty() は現在のシーンツリーを標準出力にダンプします。ノードが想定した場所にあることを確認する最も手早い方法です。エディターの リモート タブには、ゲームの実行中にライブのシーンツリーが表示されます。@tool ノードでは、_get_configuration_warnings() を実装して、エディターの SceneTree ドックに設定エラーを表示します。
詳しい例(print_tree_pretty のパターン、ノードグループのデバッグヘルパー、_get_configuration_warnings の GDScript と C#)については、references/scene-tree-debugging.md を参照してください。
7. 系統的なデバッグ手法
プリント出力、ブレークポイント、リモートでの調査ですぐにバグが見つからない場合は、意識的なプロセスに切り替えます。再現 → 切り分け → 仮説 → トレース → 修正 → 検証 → テストの追加の順で進めます。各ステップを完了してから次へ進みます。先に進みすぎると時間を無駄にします。
各ステップの具体的な手法(二分探索、最小再現、回帰テストのパターン)を含む7ステップの手法については、references/systematic-method.md を参照してください。
8. 実装チェックリスト
- 詳細なフレーム単位の出力は
OS.is_debug_build()で保護してください。print()もprint_debug()もリリースビルドでは削除されません。 - 無効な状態や回復可能な問題には、
print()ではなくpush_error()/push_warning()を使用してください。これらにはスタックトレースが含まれます。 - プリント出力をあちこちに追加する代わりに、
F9またはbreakpoint文でブレークポイントを設定して実行を一時停止してください。 - シグナルが正しく接続されていると思い込まず、
get_signal_connection_list()でシグナルの接続を確認してください。 - デバッグセッション中に Scene → Remote を開いてライブのシーンツリーを調べ、実行時のノード状態を確認してください。
- 最適化の前に Debugger → Profiler を開いて Self 時間を測定し、まず実際のボトルネックを特定してください。
- Debugger → Monitors で Object Count または Video RAM が増加していないか確認し、メモリーリークの兆候を調べてください。
- 待機中にノードが解放された場合に備え、
awaitの後に実行されるコードではis_instance_valid()を使用してください。 - 再現 → 切り分け → 仮説 → トレース → 修正 → 検証 → テストの順に従ってください。修正に先走らないでください。
- バグを修正したら、同じ不具合が気づかないうちに再発しないよう、名前付きの回帰テストを作成してください。