mcpField
G
godot-optimization

godot-optimization

github.com/jame581開発926214 回インストール2 週間前に更新
リポジトリ

godot-optimization について

GDScriptまたはC#を使うGodot 4.3以降のプロジェクト向け最適化スキルです。ボトルネックの計測方法と、描画、物理、スクリプト、メモリの調整方法を説明します。

主な機能

  1. 01Profilerとモニターの使い方
  2. 02ドローコールの削減方法
  3. 03物理処理の調整パターン
  4. 04GDScriptとC#のホットパス対策
  5. 05メモリ管理とオブジェクトプール

活用シーン

  1. 01フレーム時間のボトルネックを見つける
  2. 02描画と物理処理の負荷を抑える
  3. 03メモリ確保や頻繁に生成するオブジェクトを管理する

インストール

コマンドをコピーしてターミナルで実行します。

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

インストール後、Claude Code で/godot-optimizationを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
cpu-bottlenecks.md
draw-calls.md
memory-management.md
physics-tuning.md

詳細

Godot の最適化

このスキルでは、GDScript と C# の両方で Godot 4.3 以降のプロジェクトのパフォーマンスを最適化する方法を扱います。組み込みプロファイラー、ドローコールの削減、物理演算の調整、GDScript のパフォーマンスパターン、メモリ管理、オブジェクトプーリング、およびよくあるボトルネックの参照表を取り上げます。

関連スキル: 体系的なデバッグとプロファイリングについては godot-debugging、パフォーマンスレビューのチェックリストについては godot-code-review、リリースビルドの最適化については export-pipeline、コリジョン形状、レイヤー、物理ボディの種類については physics-system、2D メッシュの最適化、パーティクルのパフォーマンス、描画順序の調整については 2d-essentials、処理をメインスレッドから移す方法については multithreading、モバイルのパフォーマンス予算については mobile-development を参照してください。


1. プロファイラーの使用

フレーム時間の予算

60 fps では、フレーム全体(更新、物理演算、レンダリング)を 16.6 ms 以内に完了する必要があります。30 fps では予算は 33.3 ms です。この予算の大部分を消費するシステムがあれば、それがボトルネックです。

目標 FPSフレーム予算
1208.3 ms
6016.6 ms
3033.3 ms
プロファイラー出力の読み方

Debugger > Profiler を開き、Start をクリックして、測定したいシナリオをプレイし、Stop をクリックします。

  • Frame Time — そのフレームにかかった合計の実時間(ミリ秒)です。
  • Self — 呼び出し先を除いた、その関数内で費やされた時間です。これは主なホットスポットの指標です。Self 時間が長い関数は、直接高コストな処理を実行しています。
  • Total — 呼び出し先をすべて含めた時間です。高コストな処理のサブツリーを特定するのに役立ちます。
  • Calls — フレームごとの呼び出し回数です。1 回ごとの処理が軽くても、1 フレームに何千回も呼び出される関数はフレーム時間を大きく占めることがあります。
  • 関数名をクリックすると、スクリプトエディターでそのソースに移動します。
# Manual micro-benchmark for a specific block
var start := Time.get_ticks_usec()
_run_expensive_operation()
var elapsed := Time.get_ticks_usec() - start
print("_run_expensive_operation: %d µs" % elapsed)

C#:

// Manual micro-benchmark using Stopwatch (high-resolution timer)
using System.Diagnostics;

var sw = Stopwatch.StartNew();
RunExpensiveOperation();
sw.Stop();
GD.Print($"RunExpensiveOperation: {sw.Elapsed.TotalMilliseconds:F3} ms");

// Alternative using Godot's built-in timer (microsecond precision)
long start = (long)Time.GetTicksUsec();
RunExpensiveOperation();
long elapsed = (long)Time.GetTicksUsec() - start;
GD.Print($"RunExpensiveOperation: {elapsed} µs");
Monitors タブ

Debugger > Monitors では、ゲームの実行中にエンジンのメトリクスをリアルタイムで確認できます。モニター名をクリックすると、ライブグラフが開きます。確認しておきたい主なモニターは次のとおりです。

モニター確認する内容
Time > FPS目標を下回っている — フレーム予算を超過しています
Time > Process高い — _process() のコールバックが高コストです
Time > Physics Process高い — _physics_process() または物理シミュレーションが高コストです
Raster > Total Draw Calls約 500(モバイル)または約 2 000(デスクトップ)を超えている — バッチ処理が必要です
Video > Video Mem継続的に増加している — テクスチャまたはメッシュが解放されていません(メモリリーク)
Object > Objectsシーンを再読み込みするたびに増加している — ノードが解放されていません
Physics 3D > Active Objectsシンプルなシーンで数が多い — ボディがスリープ状態になっていません
# Query any monitor at runtime from code
var fps := Performance.get_monitor(Performance.TIME_FPS)
var draw_calls := Performance.get_monitor(Performance.RENDER_TOTAL_DRAW_CALLS_IN_FRAME)
var video_ram := Performance.get_monitor(Performance.RENDER_VIDEO_MEM_USED)
print("FPS: %d | Draw calls: %d | VRAM: %.1f MB" % [fps, draw_calls, video_ram / 1_048_576.0])

C#:

// Query any monitor at runtime from code
double fps = Performance.GetMonitor(Performance.Monitor.TimeFps);
double drawCalls = Performance.GetMonitor(Performance.Monitor.RenderTotalDrawCallsInFrame);
double videoRam = Performance.GetMonitor(Performance.Monitor.RenderVideoMemUsed);
GD.Print($"FPS: {fps:F0} | Draw calls: {drawCalls:F0} | VRAM: {videoRam / 1_048_576.0:F1} MB");

2. ドローコールの最適化

個別のメッシュ、スプライト、またはキャンバスアイテムのうち、隣接する要素とバッチ処理できないものは、それぞれ 1 回のドローコールを消費します。ドローコールの削減は、特にモバイルでは最も効果の大きい最適化の 1 つです。2D のバッチ処理は、隣接するアイテムがテクスチャとマテリアルを共有している間は自動で行われます(Compatibility、および 4.4 以降の Forward+/Mobile)。そのため、アトラススプライトを使い、固有のマテリアル数を抑え、画面外の処理をカリングしてください。CanvasGroup はバッチ処理されず、子要素をバックバッファにレンダリングします。

詳しい手順(2D のバッチ処理を妨げる条件、インスタンスごとのユニフォームを使った共有マテリアル、テクスチャアトラス、VisibleOnScreenNotifier2D/3D によるカリング、3D の LOD 切り替え)については、references/draw-calls.md を参照してください。


3. 物理演算の最適化

物理演算の調整では、ブロードフェーズの処理を最小限に抑え、移動するボディにメッシュコライダーを使わないことが重要です。各ボディで実際に必要なレイヤーだけにコリジョンマスクを絞り、移動するものには ConcavePolygonShape3D の代わりにプリミティブを使用し、重なりの検出にはフレームごとのレイキャストよりも Area2D/3D を優先してください。

詳しい手順(レイヤー/マスクのビット例、コリジョン形状のコスト表、Engine.physics_ticks_per_second の調整、Area とレイキャストの使い分け)については、references/physics-tuning.md を参照してください。


4. GDScript のパフォーマンス

ホットパスの GDScript を高速化するには、フレームごとのメモリ確保をなくし、String ではなく StringName を比較し、型付き配列/PackedArrays を使い、リソースをクラススコープで preload するのが効果的です。同じメモリ確保の原則は C# にも当てはまります(型付きの Array[T] の代わりに List<T> を使い、static readonly StringName フィールドを使用します)。

詳しい手順(キャッシュ済みグループクエリ、再利用するベクトルのローカル変数、&"..." リテラル、PackedVector2Array、静的型付け、preload と load の比較、さらに C# での同等の実装例)については、references/cpu-bottlenecks.md を参照してください。


5. メモリ管理

シーンの再読み込みをまたいで Performance.MEMORY_STATIC と OBJECT_COUNT を監視してください。値が増え続ける場合は、参照がリークしています。パスから読み込んだリソースはキャッシュされ、共有されます。インスタンスごとに変更する必要がある場合は、.duplicate() を呼び出してください。ノードには必ず queue_free() を使ってください。シグナルを発行中のノードに対する free() は、エディターとデバッグエクスポートでは拒否されます(「Object is locked and can't be freed」)。しかし、リリースエクスポートではそのチェックが省略され、シグナルの発行中にノードが解放されます(動作は未定義です)。

詳しい手順(Performance シングルトンのクエリ、ResourceLoader のキャッシュの仕組み、queue_free と free の比較表、さらに弾丸/エフェクト/パーティクル向けの GDScript と C# による完全なオブジェクトプール実装)については、references/memory-management.md を参照してください。


6. よくあるボトルネック

問題診断ツール対処法
ドローコールが多すぎるDebugger > Monitors Raster > Total Draw Calls、Viewport > Debug > Draw Calls オーバーレイ2D の隣接要素で同じテクスチャとマテリアルを使ってバッチ化します。3D では MultiMeshInstance3D または結合したメッシュを使います。テクスチャアトラスを使用し、固有のマテリアルを減らします
_process 内の GDScript が重いProfiler の Self 列で、スクリプト関数が上位に表示されますロジックを _physics_process に移します(デフォルトでは 60 ティック/秒に固定されているため、高フレームレート時の _process より呼び出し頻度が低くなります)。クエリをキャッシュし、フレームごとのメモリ確保を避け、ループ処理が厳しい場合は C# を検討します
シグナル接続が過剰Profiler でシグナルのディスパッチによるオーバーヘッドを確認します。get_signal_connection_list() を手動で監査します冗長な接続を削除します。高頻度データではフレームごとのシグナルよりポーリングを優先し、一度だけ実行して終わる処理には CONNECT_ONE_SHOT を使います
TileMap が最適化されていないマップが画面に表示されている間、ドローコール数が多いレイヤー数を減らします。レイヤーごとに単一のアトラステクスチャを使います。旧式の TileMap の代わりに TileMapLayer(Godot 4.3 以降)を使います
非圧縮テクスチャのサイズが大きいMonitors の Video > Video Mem の値が高くなっています。Import ドックでテクスチャ設定を確認しますImport ドックでテクスチャ圧縮(VRAM Compressed)を有効にし、ミップマップを使います。近くで表示しないアセットは解像度を半分にします
有効な物理ボディが多すぎるMonitors の Physics 3D > Active Objects の値が高くなっています。Profiler で _physics_process が遅いことを確認しますRigidBody3D でスリープを有効にします(can_sleep = true)。物理ティックレートを下げ、遠くにあるボディを疑似アニメーションに置き換えます。レイヤー/マスクを使って衝突チェックの範囲を絞ります
ホットパスでの文字列操作Profiler で文字列処理の多い関数の Self 時間を確認します。C# では GC による一時停止が頻発しますString の比較を StringName(&"...")に置き換えます。_process 内での String のフォーマットを避け、文字列を一度だけ作成してキャッシュします
ホットパスでの instantiate()Profiler で PackedScene.instantiate の Self 時間が長いことを確認します(Editor Settings > Debugger > Profile Native Calls を有効にします)オブジェクトプールを実装します(references/memory-management.md を参照)。起動時にシーンをプリロードし、ゲームプレイ中ではなくローディング画面中に生成します

7. よくあるアンチパターン

  • _process 内での割り当て — フレームごとに新しい Array、Dictionary、String、または Vector のコンストラクターを呼び出します。コンテナーをキャッシュし、フィールドをその場で変更してください。references/cpu-bottlenecks.mdを参照してください。
  • 移動するボディでのメッシュコライダー — CharacterBody3D または RigidBody3D に ConcavePolygonShape3D を使用しています。代わりにカプセル、ボックス、または凸包を使用してください。references/physics-tuning.mdを参照してください。
  • インスタンスごとに異なるマテリアル — _ready() 内の material_override = SomeMaterial.new() はバッチ処理を妨げます。マテリアルを共有し、シェーダーパラメーターで変化を付けてください。references/draw-calls.mdを参照してください。
  • 処理負荷の高い箇所での load() — _process または _physics_process から load("res://...") を呼び出しています。クラススコープで const X := preload(...) を使用してください。references/cpu-bottlenecks.mdを参照してください。
  • 寿命の短いオブジェクトに対する instantiate() と queue_free() — 弾丸、ヒットエフェクト、パーティクルなどです。プールを使用してください。references/memory-management.mdを参照してください。

8. チェックリスト

リリース前、またはパフォーマンスに関する問題を調査する際に、このリストを確認してください。

プロファイラー

  • 最も負荷の高いゲームプレイシナリオでプロファイラーを実行します。
  • 単一の関数の Self time がフレーム予算の 30% を超えていないことを確認します。
  • 合計フレーム時間が予算内(60 fps で 16.6 ms)に収まっていることを確認します。

ドローコール

  • ドローコール数が目標値以内(モバイルでは ≤500、デスクトップでは ≤2 000)です。
  • 隣接して描画される 2D スプライトは、バッチ処理されるよう、1 つのアトラステクスチャとマテリアルを共有しています。
  • 可能な場合、テクスチャはアトラスにまとめられ、重複するマテリアルは取り除かれています。
  • 画面外のノードは VisibleOnScreenNotifier2D/3D を使用して処理を一時停止します。
  • 3D メッシュでは、インポート設定または手動の切り替えロジックによって LOD が有効になっています。

物理

  • コリジョンレイヤーとマスクは必要最小限です。各ボディは不要なレイヤーをチェックしません。
  • 移動するボディで ConcavePolygonShape を使用していません。カプセル、ボックス、または凸形状に置き換えられています。
  • ゲームの種類に適した物理演算のティックレートになっています(ターン制やトップダウン型では 30 Hz で問題ない場合があります)。
  • フレームごとのレイキャストではなく、重なりの検出に Area2D/3D を使用しています。
  • 該当する RigidBody3D ノードでは can_sleep = true に設定されています。

GDScript

  • _process または _physics_process 内で Array、Dictionary、String を割り当てていません。
  • 処理負荷の高い箇所での文字列比較には、すべて StringName(&"...")を使用しています。
  • 処理負荷の高い箇所で使う配列は、すべて型指定されています(Array[T] または PackedArray)。
  • 処理負荷の高い箇所で使う関数の引数と戻り値には、すべて静的な型が指定されています。
  • シーンとリソースの参照には、フレームごとの load ではなく、クラススコープで preload を使用しています。

メモリ

  • Performance.get_monitor(Performance.MEMORY_STATIC) の値は、シーンの再読み込み前後で安定しています。
  • インスタンスごとの変更が必要なリソースには .duplicate() を使用しています。
  • 同期的な破棄が明示的に必要な場合を除き、すべてのノードの削除に queue_free() を使用しています。

オブジェクトプーリング

  • 弾丸、ヒットエフェクト、パーティクルなど、頻繁に生成されるオブジェクトにはプールを使用しています。
  • 通常のゲームプレイ中に実行時の拡張が不要になるよう、プールの初期サイズを十分大きくしています。
  • プールされたオブジェクトは、再有効化時に位置、速度、シグナルなど、すべての状態をリセットします。

コメント