godot-master について
ゲームやアプリの設計・実装を計画する Godot 開発者向けのエンジニアリング知識ハブです。意思決定フレームワークやワークフロー、分野別スキルの連鎖で構成され、Godot 4.7+ を対象とします。
主な機能
- 01アーキテクチャの意思決定フレームワーク
- 02状態とシグナルのレイヤーパターン
- 03プロジェクトのスキャフォールディング手順
- 04ジャンルとプラットフォーム別の案内
- 05ノードとデータ型の選択
活用シーン
- 01Godot プロジェクトの構成計画
- 02新規プロジェクトの骨組み作成
- 03ゲームジャンルに合う実装パターンの選択
- 04関連する分野別スキルへのタスク振り分け
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ thedivergentai/ game-design-agentic-skills --skill godot-masterインストール後、Claude Code で/godot-masterを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/2d-animation-cutout-and-skeletal.md2d-animation-expert-techniques.md2d-animation-migration-notes.md2d-animation-signals-and-frame-events.md2d-animation.md2d-physics-area2d-and-queries.md2d-physics-collision-layers-masks.md2d-physics-migration-notes.md2d-physics.md3d-lighting-environment-and-fog.md3d-lighting-gi-and-bake-recipes.md3d-lighting-migration-notes.md3d-lighting.md3d-materials-migration-notes.md3d-materials-pbr-workflows.md3d-materials.md3d-world-building-gridmap-and-csg.md3d-world-building-migration-notes.md3d-world-building-streaming-and-procgen.md3d-world-building.mdability-system-elite-ability-patterns.mdability-system-migration-notes.mdability-system.mdadapt-2d-to-3d-migration-notes.mdadapt-2d-to-3d-migration-recipes.mdadapt-2d-to-3d.mdadapt-3d-to-2d-dimension-reduction.mdadapt-3d-to-2d-migration-notes.mdadapt-3d-to-2d.mdadapt-desktop-to-mobile-migration-notes.md詳細
Godot Master:リードアーキテクト向けナレッジハブ
すべてのセクションは、ナレッジの差分、つまりベースモデルがすでに知っていることと、実際の製品を出荷してきたシニア Godot エンジニアが知っていることのギャップに焦点を当てることで、そのトークンに見合う価値を提供します。
ライブラリの対象 — Godot 4.7+
すべてのドメインスキルのミラーは Godot 4.7+(安定版)を対象とします。エンジンのバージョンアップグレード(1.x/2.x のレガシー版 → 3 → 4 → 4.x の段階的アップグレード)には、godot-version-migration を使用してください。このハブを移行履歴として扱わないでください。
各機能へ案内する際に留意すべき 4.7 の横断的な注意事項:AreaLight3D/HDR → 3D ライティング;Asset Store と Asset Library の違い → エクスポート/プラットフォームモジュール;RichTextLabel の ImageUnit、入力デバイス ID 定数、Jolt の挙動 → 移行ハブのモジュールノート。
🧠 パート 1:エキスパートの思考フレームワーク
「誰が何を所有するのか?」 — アーキテクチャの健全性チェック
システムを書く前に、状態の各要素について、次の 3 つの質問に答えてください。
- データを所有するのは誰か?(
CombatSystemではなく、StatsComponentが体力を所有する) - 変更を許可されているのは誰か?(
apply_damage()のような公開メソッドを介して、所有者だけが変更できる) - 変更されたことを知る必要があるのは誰か?(
health_changedシグナルをリッスンしているすべての人)
すべての状態変数についてこの 3 つすべてに答えられない場合、アーキテクチャには結合の問題があります。これは OOP のカプセル化ではありません。Godot 固有の考え方です。なぜなら、アクセス修飾子ではなく、シグナルシステムそのものが強制の仕組みだからです。
Godot の「レイヤーケーキ」
すべての機能を 4 つのレイヤーに整理してください。シグナルは上方向にのみ伝わり、下方向には伝わりません。
┌──────────────────────────────┐
│ PRESENTATION (UI / VFX) │ ← Listens to signals, never owns data
├──────────────────────────────┤
│ LOGIC (State Machines) │ ← Orchestrates transitions, queries data
├──────────────────────────────┤
│ DATA (Resources / .tres) │ ← Single source of truth, serializable
├──────────────────────────────┤
│ INFRASTRUCTURE (Autoloads) │ ← Signal Bus, SaveManager, AudioBus
└──────────────────────────────┘
重要なルール:プレゼンテーションはデータを直接変更してはなりません。インフラストラクチャはシグナルのみを介してやり取りします。Label ノードが player.health -= 1 を呼び出しているなら、アーキテクチャは破綻しています。
シグナルバスの階層型アーキテクチャ
- グローバルバス(Autoload):ライフサイクルイベント(
match_started、player_died、settings_changed)にのみ使用します。デバッグの煩雑化が代償となるため、イベント数は 15 未満に抑えてください。 - スコープ付き機能バス:各機能フォルダーに専用のバスを設けます(例:
CombatBusは戦闘ノード専用)。これが、規模を拡大できる折衷案です。 - 直接シグナル:単一シーン内の親子間の通信に使用します。シーンの境界を越えて使用してはいけません。
🔗 「スマートな相互接続」の指針
エキスパートシステムは、孤立していることではなく、ペイロードの合成によって定義されます。
- 物理演算 → パフォーマンス:
PhysicsServer2DとRenderingServerは、SceneTreeのノードによるオーバーヘッドを回避します。1,000 個以上の弾丸やパーティクルを扱う場合に使用すると、O(1) の処理を実現できます。 - アニメーション → 物理演算:
AnimationTree.get_root_motion_position()は、アニメーションによる変位を物理演算のvelocityに変換し、複雑な移動時の「足滑り」を防ぎます。 - データ → リアクティビティ:シリアライズされた
Resourceオブジェクト(Statsなど)は、変更時にシグナルを発行するため、密結合を避けながら UI を自動的に更新できます。 - アセット → 生成:
ResourceLoaderの非同期フェーズ中にプリロードする O(1) の Dictionary ベースのキャッシュにより、アイテムや敵の生成時に発生する I/O のひっかかりを防ぎます。 - モバイル → ビジュアル:ロード画面中に非表示のエフェクトをインスタンス化して GPU シェーダーパイプラインのコンパイルを強制し、実行時のフレームのひっかかりを防ぎます。
- ネットワーク → 帯域幅:パケットを 100 バイト未満に保つため、同期には JSON/文字列ではなく、
PackedByteArrayへのビットパッキングを使用します。 - ジャンルの合成:
Shooter:100 倍のパフォーマンスを得るため、RayCast3Dノードではなく、必ずintersect_ray()(直接空間状態)を使用します。RPG:エンドゲームの進行を維持するため、ダメージはbase * pow(scaling, level)に従います。RTS:隊形の整合性を保つため、Relative Offsetを使って重心を基準にグループを移動させます。Metroidvania:シームレスなルーム切り替えにはResourceLoader.load_threaded_request()を使用します。Platformer:プロフェッショナルな操作感を実現するには、Jump Buffering(約 0.15 秒)とCoyote Timeが必須です。Simulation:Tick Managerでバッチ処理を行います。数千のユニットを維持するには、エンティティごとの_processを避けてください。Romance:複雑な物語の分岐を表現するため、Multi-Axial Affection(魅力、信頼、安心感)を使用します。Architecture:シーン間の循環結合をなくすため、Signal Architectureは「シグナルは上へ、呼び出しは下へ」という原則に厳密に従います。
🧭 パート 2:アーキテクチャ上の意思決定フレームワーク
マスター意思決定マトリクス
| シナリオ | 戦略 | 必須スキルチェーン | トレードオフ |
|---|---|---|---|
| ラピッドプロトタイプ | イベント駆動型モノリス | 読む:基礎 → 自動読み込み。ジャンルやプラットフォームのリファレンスは読み込まないでください。 | 素早く開始できますが、スパゲッティ化のリスクがあります |
| 複雑なRPG | コンポーネント駆動型 | 読む:コンポジション → 状態 → RPGステータス。マルチプレイヤーやプラットフォームのリファレンスは読み込まないでください。 | セットアップに手間がかかりますが、無限に拡張できます |
| 大規模オープンワールド | リソースストリーミング | 読む:オープンワールド → セーブ/ロード。パフォーマンスも読み込みます。 | I/Oが複雑で、10Kユニットを超えると浮動小数点精度の揺らぎが発生します |
| サーバー認証型マルチプレイヤー | 決定論的 | 読む:サーバーアーキテクチャ → マルチプレイヤーネットワーク。シングルプレイヤー向けのジャンルリファレンスは読み込まないでください。 | レイテンシが高くなりますが、チート対策を強化できます |
| モバイル/Web移植 | アダプティブ・レスポンシブ | 読む:UIコンテナ → デスクトップからモバイルへの適応 → モバイルプラットフォーム。 | UIが複雑になりますが、幅広く展開できます |
| アプリケーション/ツール | アプリコンポジション | 読む:アプリのコンポジション → テーマ設定。ゲーム固有のリファレンスは読み込まないでください。 | ゲームとは異なるパラダイムです |
| 恋愛/恋愛シミュレーション | 好感度エコノミー | 読む:恋愛 → 会話 → UIリッチテキスト。 | UIとナラティブの密度が高くなります |
| 秘密/イースターエッグ | 意図的な難読化 | 読む:秘密 → 永続化。 | コミュニティの参加を促せますが、デバッグのリスクがあります |
| 収集クエスト | 探索ロジック | 読む:コレクション → Marker3Dの配置。 | プレイヤーの定着と探索意欲を促します |
| 季節イベント | ランタイム注入 | 読む:イースターのテーマ設定 → マテリアルの切り替え。 | 素早くブランドを演出でき、アセットを汚しません |
| ソウルライクの死亡システム | リスクと報酬のある復活 | 読む:復活/死体回収 → 3D物理。 | 緊張感が高まりますが、プレイヤーにストレスを与えるリスクがあります |
| ウェーブ制アクション | 戦闘ペースのループ | 読む:ウェーブ → 戦闘。 | 緊張感を高め、エンカウントを設計できます |
| バランス/難易度/エコノミーの進行調整 | モンテカルロ・バランスラボ | 読む:リソース → エコノミー → 戦闘/RPGステータス/ウェーブ(必要に応じて) → モンテカルロバランサー → テスト → ビルダー。 | 統計的な厳密さを確保できます。抽象シミュレーションはヘッドレスGodotと照らし合わせて調整する必要があります |
| サバイバルエコノミー | 採集ループ | 読む:採集 → インベントリ。 | リソースを希少に保ち、ループを持続させます |
| レース/スピードラン | 検証ループ | 読む:タイムトライアル → 入力バッファ → レースジャンル。 | 高い精度を実現し、ゴースト記録への挑戦意欲を促します |
| ホラー/ステルス | 緊張感の管理 | 読む:ホラージャンル → ステルスジャンル → オーディオ。 | 雰囲気を作り、プレイヤーを無防備な状態にします |
| カード/ボードゲーム | ルールの適用 | 読む:カードゲームジャンル → ターンシステム。 | 状態が決定論的ですが、UIの負荷が高くなります |
| シミュレーション/RTS | バッチ処理 | 読む:シミュレーションジャンル → RTSジャンル → パフォーマンス。 | 多数のユニットを扱い、O(1)のロジックを実現します |
| HDR/シネマティックなビジュアル | ディスプレイパイプライン | 読む:3Dライティング → デスクトッププラットフォーム → シェーダーの基礎。プロジェクト設定でビューポートのHDRを有効にします。 | プラットフォームごとにトーンマッピングを調整する必要があります |
| 矩形エリアライト | AreaLight3D | 読む:3Dライティング → 3Dマテリアル。発光マテリアルとGIのハックよりもAreaLight3Dを優先してください。 | 最高品質で使用するにはForward+レンダラーが必要です |
| モバイルのタッチ操作 | ネイティブジョイスティック | 読む:モバイルプラットフォーム → デスクトップからモバイルへの適応。組み込みの仮想ジョイスティックを使用します(4.7+)。 | プラグインへの依存を減らせます |
| アドオン/アセットの探索 | Asset Store | 読む:プロジェクトの基礎 → ビルドのエクスポート。Asset StoreはAsset Libraryに置き換わります。 | ストアのUIはベータ版のため、アドオンごとにライセンスを確認してください |
| エージェントの視覚確認/ビジュアルQA | キャプチャ → WebP → 評価基準 | 読む:エージェントビジョン。スクリーンショットアセット、ウィンドウ/領域/画面、または一時的なエディターブリッジでキャプチャし、その後、構造化レビューを行います。ジャンルのリファレンスは読み込まないでください。 | ホスト側のみで実施します。Autoloadに追加せず、ステージング済みアドオンとして残さないでください |
「Nodeを使わない場合」の判断
専門家だけが行う判断の中でも、特に大きな影響を与えるものです。Godotのドキュメントでは、「あらゆるものにノードを使うのは避ける」と明記されています。
| 種類 | 使用する場面 | コスト | 専門的なユースケース |
|---|---|---|---|
Object | カスタムデータ構造、手動メモリ管理 | 最も軽量です。.free()を手動で呼び出す必要があります。 | カスタム空間ハッシュマップ、ECSのようなデータストア |
RefCounted | 一時的なデータパケット、自動削除されるロジックオブジェクト | 参照がなくなると自動的に削除されます。 | DamageRequest、PathQuery、AbilityEffect — シーンツリーを必要としないロジックパケット |
Resource | インスペクターに対応したシリアライズ可能なデータ | RefCountedより少し重くなります。.tresの入出力を処理します。 | ItemData、EnemyStats、DialogueLine — デザイナーがインスペクターで編集する必要があるあらゆるデータ |
Node | _process/_physics_processが必要、シーンツリー内に存在する必要がある | 最も重くなります — ノードごとにSceneTreeのオーバーヘッドが発生します。 | フレームごとの更新や空間トランスフォームが必要なエンティティに限ります |
専門家向けのパターン:すべてのロジックパケットとデータコンテナにRefCountedサブクラスを使用します。空間ツリー内に存在しなければならないものに限り、Nodeを使用します。これにより、複雑なシステムのシーンツリーのオーバーヘッドを半減できます。
🔧 パート3:コアワークフロー
ワークフロー1:プロフェッショナルな足場づくり
空のプロジェクトから、本番環境に対応したコンテナまで。
必須 — ファイル全体を読むこと:基礎
- クラスの種類ではなく、機能(
/features/player/、/features/combat/)ごとに整理します。player/フォルダーには、プレイヤーのシーン、スクリプト、リソース、テストを格納します。 - 読むこと:シグナルアーキテクチャ — 15個未満のイベントを持つ
GlobalSignalBusオートロードを作成します。 - 読むこと:GDScriptの習得 — Project Settings → GDScript → Debuggingで、
untyped_declaration警告を有効にします。 - 基本の
.gitignore、エクスポートプリセット、入力マップには、**プロジェクトテンプレート**を適用します。 - Godot CLIを使ってシーン階層をプログラムで生成するには、Builder(
create_scene.py、add_node.py、save_scene.py)を使用します。
注意
ワークフロー1で絶対にしてはいけないこと
- ロジックスクリプトで
res://パスを絶対に使わないでください。リソースを移動しても有効な状態を保つには、@export_fileまたは@export_dirを使用します。_init()で子ノードを初期化するのは絶対に避けてください。シーンツリーの準備ができていません。_ready()または@onreadyを使用します。Physics Ticksに「デフォルト」のプロジェクト設定を絶対に使い続けないでください。一貫性を保つには60に設定するか、適応型ロジックにはEngine.physics_ticks_per_secondを使用します。- デバッグのために
_process()内でprint()を絶対に使わないでください。フレーム時間の急増を避けるには、Debuggerまたはpush_error()を使用します。
足場づくりの際に、戦闘、マルチプレイヤー、ジャンル、プラットフォームのリファレンスを読み込まないでください。
ワークフロー 2:エンティティのオーケストレーション
モジュール化され、テスト可能なキャラクターを構築します。
必須の順序 — すべて読むこと:Composition → State Machine → CharacterBody2D または Physics 3D → Animation Tree エンティティの作業では、UI、Audio、Save/Load のリファレンスを読み込まないでください。
- ステートマシンは
InputComponentに問い合わせます。入力を直接処理することはありません。これにより、リファクタリングなしで AI とプレイヤーを切り替えられます。 - ステートマシンが処理するのは遷移のみです。ロジックはコンポーネントに配置します。
MoveStateがMoveComponentに動作するよう指示し、その逆にはしません。 - すべてのエンティティはF6 テストに合格する必要があります。「現在のシーンを実行」(F6)を押してもクラッシュしないことを確認してください。クラッシュする場合、そのエンティティにはシーン外部の依存関係があります。
注意
ワークフロー 2 で絶対にしてはいけないこと
- 絶対に
parent.do_thing()を呼び出さないでください。親が変更されると、エンティティが壊れます。代わりにシグナルrequest_actionを発行してください。- 絶対に移動に
_processを使用しないでください。可変リフレッシュレートのモニターでのちらつきを避けるため、_physics_processを使用してください。- 絶対にアニメーション名をハードコードしないでください。
AnimationPlayerで簡単に名前を変更できるよう、StringName定数またはResourceマップを使用してください。- 絶対に絶対パスで
get_node()を使用しないでください。ツリーのリファクタリングに耐えられるよう%UniqueNameを使用してください。
ワークフロー 3:データ駆動型システム
リソースを介して Combat、Inventory、Stats を連携させます。
必須の順序 — すべて読むこと:Resource Patterns → RPG Stats → Combat → Inventory
Resourceを継承するItemData.gdを 1 つ作成します。スクリプトを 100 個作る代わりに、これを 100 個の.tresファイルとしてインスタンス化します。- HUD は Player を直接参照しません。Signal Bus 上の
player_health_changedをリッスンします。 - すべての
@export Resource変数で「Local to Scene」を有効にするか、_ready()でresource.duplicate()を呼び出してください。これを怠ると、Part 8 の Bug #1 の原因になります。
注意
ワークフロー 3 で絶対にしてはいけないこと
- 絶対にSignal Bus で
Nodeの参照を渡さないでください。オブジェクトは解放されるため、長期的な追跡には RID または ID のほうが安全です。- 絶対に実行時にコードから
.tresファイルを変更しないでください(ディスク上のファイルが変更されます)。変更前に必ず.duplicate()を使用してください。- 絶対に検索頻度の高い処理に
Arrayを使用しないでください。O(1) で検索できるStringNameキーのDictionaryを使用してください。- 絶対にアイテム数や正確なリソース管理に
floatを使用しないでください。intを使用し、表示用にスケーリングしてください。
ワークフロー 4:永続化パイプライン
必須:Autoload Architecture → Save/Load → Scene Management
- Dictionary マッピングを使用してシリアライズします。新しいフィールドを追加しても古いセーブファイルが壊れないよう、
.get("key", default_value)を使用してください。 - プロシージャルワールドでは、マップ全体ではなく Seed と変更内容の Delta-List を保存します。100MB のワールドを 50KB のセーブデータにできます。
注意
ワークフロー 4 で絶対にしてはいけないこと
- 絶対に
ObjectやNodeのインスタンス全体を保存しないでください。一時的なポインターが含まれています。データをDictionaryまたはカスタムResourceに抽出してください。- 厳密な型付けが必要なデータ(例:
Vector2)に 絶対にJSONを使用しないでください。構造化された Godot 型にはvar_to_bytesまたはConfigFileを使用してください。- オートセーブのためにメインスレッドを絶対にブロックしないでください。大きな Dictionary のシリアライズには
ThreadまたはWorkerThreadPoolを使用してください。- エクスポート済みプロジェクトで
res://に絶対に保存しないでください。永続データには必ずuser://を使用してください。
ワークフロー 5:パフォーマンスの最適化
必須:デバッグ/プロファイリング → パフォーマンスの最適化
診断を優先するアプローチ(決して闇雲に最適化しないでください):
- スクリプトの処理時間が長い → 組み込みの Profiler でプロファイリングします。何百ものノードで
_processが呼び出されていないか確認します。単一マネージャーパターンまたは Server API に切り替えます(Part 6 を参照)。 - ドローコールが多い → 繰り返しのジオメトリには
MultiMeshInstanceを使用します。ORM テクスチャでマテリアルをバッチ化します。 - 物理演算によるカクつき → コリジョンをプリミティブ形状に簡略化します。2D Physics または 3D Physics を読み込みます。移動処理に
_physics_processではなく_processを使用していないか確認します。 - VRAM の過剰使用 → テクスチャを VRAM 圧縮に切り替えます(デスクトップでは BPTC/S3TC、モバイルでは ETC2)。PNG を未圧縮のまま出荷してはいけません。
- 断続的なフレームスパイク → 通常は GC パス、同期的な
load()、または NavigationServer の再計算が原因です。ResourceLoader.load_threaded_request()を使用します。
注意
ワークフロー 5 の禁止事項
- 決して
_process内でget_nodes_in_group()を使用しないでください。毎フレーム O(n) の処理が発生します。配列を_ready()でキャッシュしてください。- 決して「Stay」ロジックに
Area2Dシグナルを使用しないでください。定期的にget_overlapping_bodies()を呼び出すか、マネージャーレベルでPhysicsServerのチェックを使用してください。- 決してプロファイリングの前に最適化しないでください。ドローコールが 2000 回あり GPU の処理を妨げているなら、スクリプトの 1ms は問題ではありません。
- ホットパスでは決して
load()を使用しないでください。必ずpreloadを使用するか、非同期読み込みにはResourceLoaderを使用してください。
ワークフロー 6:クロスプラットフォーム対応
必須:入力処理 → デスクトップからモバイルへの対応 → モバイルプラットフォーム 必要に応じて以下も読む:デスクトッププラットフォーム、Web プラットフォーム、コンソールプラットフォーム、VR プラットフォーム。
- すべての入力タイプを正規化されたアクションに変換する
InputManagerオートロードを使用します。Input.is_key_pressed()を直接読み取ってはいけません。コントローラーとタッチに対応できなくなります。 - モバイルのタッチターゲット:物理サイズで最低 44px にします。ノッチや切り欠きのあるデバイスでは、Safe Area のロジックとともに
MarginContainerを使用します。 - Web エクスポート:Godot の
AudioServerは最初の再生前にユーザー操作を必要とします(ブラウザーのポリシーによる制約です)。「クリックして開始」画面で対応します。
注意
ワークフロー 6 の禁止事項
- 決して機能の検出に
OS.get_name()を使用しないでください。「SteamDeck」のようなサブセットに対応するには、OS.has_feature("mobile")またはカスタム機能タグを使用します。- 決して特定のアスペクト比を前提にしないでください。必ず
Anchorノードと組み合わせてExpandまたはKeep Aspectを使用します。- 決してGLES3/Compatibility の代替パスなしで、デスクトップ専用シェーダー(複雑な深度サンプリングなど)をモバイル/Web で使用しないでください。
- デスクトップビルドで
physical_keycodeを無視してはいけません。これにより、キーボード配列(AZERTY/QWERTY)が移動操作に影響しないようにできます。
- サニタイズされていない文字列を決して
JavaScriptBridge.eval()に渡さないでください。これにより、Web ビルドでのスクリプトインジェクションを防げます。sanitize_js_string()ヘルパーを使用してください。
ワークフロー 7:プロシージャル生成
必須:プロシージャル生成 → タイルマップを極める または 3D ワールド構築 → ナビゲーション
- 決定論的な生成を行うため、固定の
seedを設定したFastNoiseLiteリソースを必ず使用してください。 - メインスレッドで NavMesh をベイクしないでください。
NavigationServer3D.parse_source_geometry_data()とNavigationServer3D.bake_from_source_geometry_data_async()を使用してください。 - 無限ワールドでは、チャンクの読み込みは必ず
WorkerThreadPoolを使用してバックグラウンドスレッドで行ってください。シーンのチャンクをツリー外で構築し、メインスレッドでadd_child.call_deferred()を呼び出してください。
注意
ワークフロー 7:絶対にしてはいけないこと
- 「背景」のノイズに対して、ノードを絶対にインスタンス化しないでください。数千個の小さなディテールには、
MultiMeshInstanceまたは_draw内の描画ループを使用してください。- 1つの変更のためにマップ全体を絶対に再生成しないでください。実際に変更された部分だけを更新する「ダーティチャンク」システムを使用してください。
concave_polygon_shapeを使用する場合、メッシュ生成と同じフレームにコリジョンを絶対に配置しないでください。物理スレッドが停止します。- すべてのユニットについて、パス検索を毎フレーム絶対に実行しないでください。
target_positionをタイマーで更新するNavigationAgentを使用してください。
ワークフロー 8:マルチプレイヤーアーキテクチャ
必須 — 読んでください:シングルプレイヤーからマルチプレイヤーへ → ネットワーキング → サーバーアーキテクチャ シングルプレイヤーのジャンル別ブループリントは読み込まないでください。
- クライアントは入力を送り、サーバーは結果を計算します。クライアントがダメージ、位置の差分、インベントリの変更を決定することは絶対にありません。
- サーバーによる再同期を伴うクライアントサイド予測を使用してください。ローカルで予測し、サーバーのスナップショットに基づいて修正します。最大約 150ms のレイテンシを隠蔽できます。
- Godot 4 では、
MultiplayerSpawnerがレプリケーションを処理します。グローバルではなく、シーンごとに設定してください。
注意
ワークフロー 8:絶対にしてはいけないこと
- 検証せずに
rpc_id(1, ...)(クライアントからサーバー)を絶対に信頼しないでください。ハッキングされたクライアントはdamage = 999999を送信できます。_processのトランスフォームを直接絶対にレプリケーションしないでください。Inputベクトルをレプリケーションし、両側で移動をシミュレートしてください。- 高頻度パケット(移動)に
TCPを絶対に使用しないでください。UDP/ENetを使用し、補間でパケットの損失に対処してください。- すべての投射物を絶対に同期しないでください。ビジュアルにはクライアントサイド予測を使用し、「発射」イベントのみを RPC で送信してください。
ReflectionProbe、VoxelGI、SDFGIの比較:プローブは低コスト/静的、VoxelGI は中程度のコスト/ベイク済み、SDFGI は高コスト/動的です。プラットフォームの予算に応じて選択してください(パート 5 を参照)。
ワークフロー 9: レスポンシブ UI とエキスパートテーマ設定 (監査済み)
必須チェーン: UI コンテナ → UI テーマ設定 → リッチテキスト → トゥイーン
- F6 の原則: すべての UI シーンは単独でテスト可能でなければなりません。
MOUSE_FILTER_STOPは背景にのみ使用し、子にはPASSを使用します。 - 余白の確保: 手動のパディングではなく、
add_theme_constant_override("separation", X)を使用します。 - 適応型スケーリング: モバイル/デスクトップのブレークポイント対応の切り替えには
responsive_layout_builder.gdを使用します。 - ライフサイクルの安全性: 同じフレームで新しい子へスクロールしないでください。
scroll_verticalを変更する前にawait get_tree().process_frameを実行します。 - データ統合:
Resource-to-UIバインディングを使用します。UI ノードはステートレスな投影レイヤーでなければなりません。
注意
ワークフロー 9 の NEVER リスト
- 絶対ピクセルオフセットを NEVER 使用しないでください。4K や小さなモバイル画面では UI が読めなくなります。
Containerのサイズ指定を使用してください。MarginContainerを NEVER 深くネストしないでください。インスペクターが使えなくなります。プロジェクト全体のマージンには単一のThemeリソースを使用してください。- UI ボタンをゲームプレイロジックに NEVER 直接接続しないでください。UI が「シグナル」を送信し、
PlayerControllerが受信します。これにより UI 削除時のクラッシュを防ぎます。- UI 要素をターゲットへ移動するために
_process()を NEVER 使用しないでください。カクつきやフレームレート依存を避けるためにTweenを使用してください。- 透明なコンテナの
mouse_filterをSTOPのまま NEVER にしないでください。背後のすべてのクリックを「食べて」しまいます。
res://プレフィックスと安全な拡張子 (.tres、.res、.theme) を検証せずに、パスに対して動的なload()を NEVER 使用しないでください — 任意のコード/リソースの実行を防ぎます。
ワークフロー 10: シネマティックライティングと VFX (監査済み)
必須チェーン: 3D ライティング → パーティクル → 3D マテリアル → シェーダー
- GI の選択: 屋内には VoxelGI、オープンワールドには SDFGI。両方を重複させた状態で NEVER 出荷しないでください。
- シャドウの予算: 影を落とす DirectionalLight は最大 2 つ。モバイルのフィルには
fake_gi_bounce.gdを使用します。 - VFX のライフサイクル: Timer よりも
finishedシグナルを使用します。非同期の GPU ストールを避けるため、restart()で再実行します。 - 最適化: GPU キャッシュとテクスチャスロットを節約するために
ORM Textureパッキング (AO/Rough/Metal) を使用します。 - バッチング: 数千のインスタンスにわたるマテリアルのバリエーションを、ドローコールのペナルティなしで実現するために
Instance Uniformsを使用します。
注意
ワークフロー 10 の NEVER リスト
CollisionShapeノードを NEVER スケーリングしないでください。物理のジッターを避けるため、Shape リソースを厳密にスケーリングしてください。- カットアウトメッシュ (葉/フェンス) に
TRANSPARENCY_ALPHAを NEVER 使用しないでください。ソートのアーティファクトを防ぐためにALPHA_SCISSORを使用してください。- ゲームプレイ中に CSG ノードを NEVER アニメーション化しないでください。高コストな CPU ジオメトリ再計算が強制されます。
- 2D ライクなゲームにリアルタイムのグローバルイルミネーション (SDFGI/VoxelGI) を NEVER 使用しないでください。
DirectionalLight2DとCanvasModulateに留めてください。Camera3Dの near/far プレーンを NEVER 無視しないでください。不適切な設定は大規模なワールドで Z ファイティングを引き起こします。
ワークフロー 11:プログラムによるシーン構築(Builder)
必須:Builder
バッチ操作または複雑なプロシージャルな足場の構築にのみ使用してください。 大規模な CLI 自動化を行う場合は、スタンドアロンの godot-builder スキルを優先してください。
- ステップ 1:ディスクに触れる前に、ノード階層を紙または Markdown に書き起こします。
- ステップ 2:
create_scene.pyを使用して、ルートノードと.tscnのパスを定義します。 - ステップ 3:子ノードには
add_node.pyを使用します。シリアライズ時に保持されるよう、すべてのノードにownerを設定してください。 - ステップ 4:シーンが問題なく読み込まれることを確認するため、必ず
run_project.pyまたはlaunch_editor.pyを呼び出してください。 - エキスパートのルール:Builder で 構造(ノード、名前、継承)を構築し、GDScript で 動作を構築します。
注意
ワークフロー 11:絶対にしてはいけないこと
- 絶対に、階層を設計せずに
add_node.pyをいきなり実行しないでください。スパゲッティ状のシーンになります。- 絶対に、スクリプトやシーンのプロパティで絶対ファイルシステムパスを使用しないでください。
res://のみを使用してください。- 絶対に、Shape リソースを割り当てずに
CollisionShape2D/CollisionShape3Dを追加しないでください。ノードだけでは何も機能しません。- バッチでシーンを書き込んだ後、
run_project.py/launch_editor.pyによる検証を絶対に省略しないでください。
セキュリティ:境界マーカーと検証
エージェントがファイルを書き込む前に、信頼できないシーン/データのテキストを取り込む場合:
- 境界マーカー:分析を
<<<CONTEXT_START>>>と<<<CONTEXT_END>>>で囲みます。 - サニタイズ:ノード名には英数字とアンダースコアのみを使用します。パスは
res://で始まる必要があります。 - 検証:変更前にシーンが存在することを確認します。
🚫 パート 4:エキスパートが絶対にしてはいけないこと
各ルールには、一見わかりにくい理由、つまり実際のリリース経験を通じてのみ学べる理由が含まれています。
- 決して
get_tree().root.get_node("...")を使用しないでください — 絶対パスは、祖先ノードの名前変更や親の変更が一度でもあると機能しなくなります。%UniqueNames、@export NodePath、またはシグナルベースの検出を使用してください。 - ループ内や
_process内で決してload()を使用しないでください — 同期的なディスク読み込みは、メインスレッド全体をブロックします。小さなアセットにはスクリプトの先頭でpreload()を、大きなアセットにはResourceLoader.load_threaded_request()を使用してください。 - 外部参照が存在する間は決して
queue_free()を実行しないでください — 参照を保持している親ノードや配列は、「Deleted Object」エラーを引き起こします。_exit_tree()で参照をクリーンアップし、解放する前にnullを設定してください。 - ゲームプレイのロジックを決して
_draw()に記述しないでください —_draw()はレンダリングスレッドで呼び出されます。ゲーム状態を変更すると、_physics_processとの競合状態が発生します。 - 1000 個以上のオブジェクトが重なり合う場合、決して
Area2Dを使用しないでください — 各オーバーラップチェックには O(n²) のブロードフェーズコストがかかります。弾幕パターンにはShapeCast2D、PhysicsDirectSpaceState2D.intersect_shape()、またはサーバー API を使用してください。 - コンポーネントから外部状態を決して変更しないでください —
HealthComponentが$HUD.update_bar()を呼び出す場合、HUD を削除するとゲームがクラッシュします。コンポーネントはシグナルを発行し、リスナーが応答方法を決定します。 _physics_process内で決してawaitを使用しないでください —awaitは実行を一時停止するため、物理ステップでフレームがスキップされます。シグナルをトリガーとして、非同期処理を別のメソッドに移してください。- 頻繁に実行される辞書の検索で
Stringキーを決して使用しないでください — String のハッシュ化には O(n) かかります。O(1) のポインター比較ができるStringName(&"key")または整数の列挙型を使用してください。 - 解放済みオブジェクトへの
Callable参照を決して保持しないでください — クラッシュが無言で発生したり、エラーがスローされたりします。_exit_tree()でシグナルを切断するか、CONNECT_ONE_SHOTを使用してください。 - 1000 個以上のエンティティに
_processを決して使用しないでください —_processの呼び出しごとに、ノード単位の SceneTree オーバーヘッドが発生します。データ構造体の配列を反復処理する単一のManager._process(データ指向パターン)を使用するか、サーバー API を直接使用してください。 - 解放される可能性があるノードで決して
Tweenを使用しないでください — Tween の実行中にノードがqueue_free()されると、エラーが発生します。_exit_tree()で Tween を停止するか、SceneTree にバインドしてください:get_tree().create_tween()。 _process内でRenderingServerやPhysicsServerにデータを要求しないでください — これらのサーバーは非同期で動作します。ゲッター関数を呼び出すと同期的な停止が発生し、パフォーマンスが大幅に低下します。これらの API は、頻繁に実行される処理で意図的に書き込み専用として設計されています。- 初期化順序のバグを取り繕うために
call_deferred()を決して使用しないでください — アーキテクチャ上の問題(ツリー順序への依存)を隠してしまいます。明示的な初期化シグナルまたは@onreadyを使って、実際の依存関係を解決してください。 - 循環するシグナル接続を決して作成しないでください — ノード A が B に接続し、B が A に接続する状態です。最初のシグナル発行時に無限ループが発生します。メディエーターパターン(シグナルバス)を使って循環を断ち切ってください。
- 継承を決して 3 階層より深くしないでください — 3 階層を超えると、
super()の呼び出しチェーンのデバッグが非常に困難になります。代わりにコンポジション(子Node)を使って動作を追加してください。 - 物理演算が多いジャンル(FPS/ARPG)では、ヒット判定や移動に決して
_processを使用しないでください。フレームレートに左右されない衝突判定を実現するため、必ず_physics_processを使用してください。 - 永続的なゲーム状態(Health、XP、Inventory)について、クライアントを権威のある情報源として決して信頼しないでください。Server-Auth または Secure Checksums のみで処理してください。
- 実行時に高頻度で行うチェックには、標準の文字列を決して使用しないでください。O(n) のハッシュ化を避けるため、必ず
StringName(&"active")を使用してください。 - ユニット数の多いゲーム(RTS/MOBA)で、RVO の回避処理を毎フレーム手動で行わないでください。
NavigationAgentの内部スレッド処理に任せてください。 - プロシージャル生成や重い I/O のために、メインスレッドを決してブロックしないでください。必ず
WorkerThreadPoolに処理を委譲してください。 - 固有のインスタンスで使用するリソース(例:敵のステータス)では、
Local-to-Sceneを決して無効にしないでください。無効にすると、すべてのインスタンス間で共有メモリのバグが発生します。 - 通貨に
floatを決して使用しないでください。複雑な経済システムでの精度のずれを防ぐため、必ず整数のセント単位を使用してください。 physics_frameより前にtarget_positionを決して設定しないでください。ナビゲーションマップは_ready()中には準備できていません。- 大きな切り抜きサーフェス(葉など)に
TRANSPARENCY_HASHやALPHAを決して使用しないでください。パフォーマンスとソートのため、ALPHA_SCISSORを使用してください。 CollisionShapeノードを決してスケーリングしないでください(Node2D/3D のスケール)。予測できない物理法線やジッターを避けるため、シェイプハンドルを使用するか、リソースのサイズを変更してください。is_on_floor()が true のときに重力を決して適用しないでください。微小なジッターが発生し、床へのスナップが妨げられます。垂直速度を必ず 0 または小さな定数にリセットしてください。- 動的シグナル(キャプチャするラムダ)の切断を決して忘れないでください。Godot はローカル変数をキャプチャするラムダを自動で切断できないため、解放済みオブジェクトでクラッシュが発生します。
- モバイルのタッチ操作にマウスイベントを決して使用しないでください。信頼性の高いマルチタッチ対応のため、必ず
InputEventScreenTouchとInputEventScreenDragを使用してください。 - モバイルで Forward+ レンダラーを決して使用しないでください。GPU のボトルネックやバッテリー消費を避けるため、必ず Mobile または Compatibility レンダラーを使用してください。
- マウスの回転量を Transform に直接決して加算しないでください。ジンバルロックや精度の低下を防ぐため、Yaw/Pitch の変数を別々に保持してください。
- 実行時に高頻度で行うチェックに標準の String を決して使用しないでください。O(n) のハッシュ化オーバーヘッドを避けるため、必ず
StringName(&"active")を使用してください。 - ゲーム状態(Health、Inventory)についてクライアントを決して信頼しないでください。クライアントはアクションを提案し、サーバーが検証してブロードキャストすることで、不正行為を防ぎます。
- 移動の更新に
ReliableRPC を決して使用しないでください。高レイテンシー環境での Head-of-Line ブロッキングを防ぐため、UnreliableOrderedを使用してください。
📊 パート 5:パフォーマンス予算(具体的な数値)
| 指標 | モバイルの目標 | デスクトップの目標 | 専門家向けメモ |
|---|---|---|---|
| ドローコール数 | < 100(2D)、< 200(3D) | < 500 | 植生や破片には MultiMeshInstance を使用します |
| 三角形数 | 表示中は < 100K | 表示中は < 1M | 500K を超える場合は LOD システムが必須です |
| テクスチャ VRAM | < 512MB | < 2GB | VRAM 圧縮:ETC2(モバイル)、BPTC(デスクトップ) |
| スクリプト実行時間 | フレームあたり < 4ms | フレームあたり < 8ms | ホットループを Server API に移動します |
| 物理ボディ数 | アクティブなものが < 200 | アクティブなものが < 1000 | 大量シミュレーションには PhysicsServer の直接 API を使用します |
| パーティクル数 | 合計 < 2000 | 合計 < 10000 | GPU パーティクルを使用し、visibility_aabb を手動で設定します |
| オーディオバス数 | 同時に < 8 | 同時に < 32 | オーディオシステムのバスルーティングを使用します |
| セーブファイルサイズ | < 1MB | < 50MB | プロシージャルワールドにはシード + 差分パターンを使用します |
| シーンの読み込み時間 | < 500ms | < 2s | ResourceLoader.load_threaded_request() |
⚙️ パート 6:Server API — 専門家向けのパフォーマンス向上策
これは、ほとんどの Godot 開発者が学ぶことのない知識です。シーンツリーがボトルネックになったら、Godot の低レベル Server API を使ってシーンツリーを完全に回避します。
Server API に切り替えるタイミング
- 10K 以上の描画インスタンス(スプライト、メッシュ):
Sprite2D/MeshInstance3Dノードの代わりに、RID を使ってRenderingServerを使用します。 - スクリプトからの操作を伴う弾幕系/パーティクルシステム:
Area2Dノードの代わりに、PhysicsServer2Dでボディを作成します。 - 大量の物理シミュレーション:ラグドール群、破片、または流体のようなシミュレーションには、
PhysicsServer3Dを直接使用します。
📊 パフォーマンス比較:SceneTree と Server API
SceneTree を回避すると、ノードのライフサイクル管理、シグナルの伝播、仮想関数(_process など)の呼び出しに伴う大きな CPU オーバーヘッドを排除できます。
| 指標 | SceneTree(ノード) | Server API(RID) | 専門家向けの理由 |
|---|---|---|---|
| オブジェクト数の上限 | 約 1,000~5,000 | 50,000 以上 | SceneTree の走査コストは O(n) です。サーバーでは O(1) の RID による直接ハンドルを使用します。 |
| メモリオーバーヘッド | ノードあたり約 2KB~10KB | RID あたり < 200 bytes | ノードはツリーの状態、シグナル、インスペクターのメタデータを保持します。RID は不透明な 24 バイトのハンドルです。 |
| CPU 時間 | 高い(仮想関数呼び出し) | 最小(直接 API) | ノードではインスタンスごとに _process を呼び出す必要があります。サーバーは C++ で操作をまとめて処理します。 |
| スレッド処理 | メインスレッドのみ | 本質的にスレッドセーフ | ほとんどの Server API はスレッドセーフです(Project Settings で有効にする必要があります)。 |
| ガベージコレクション | 自動(RefCounted) | 手動(割り当て/解放) | サーバーでは手動のライフサイクル管理(RID の作成/削除)が必要です。 |
専門家向けメモ:RID を使うと、未加工のデータを管理し、エンジンのコアロジックと直接やり取りできます。SceneTree の管理処理がボトルネックとなる弾幕系、大量の植生、複雑なプロシージャルシミュレーションにおいて、これは主な「切り札」となります。
RID パターン(専門家向け)
Server API は RID(Resource ID)を介して通信します。RID はサーバー側オブジェクトへの不透明なハンドルです。重要なルール:
# Create server-side canvas item (NO node overhead)
var ci_rid := RenderingServer.canvas_item_create()
RenderingServer.canvas_item_set_parent(ci_rid, get_canvas_item())
# CRITICAL: Keep resource references alive. RIDs don't count as references.
# If the Texture resource is GC'd, the RID becomes invalid silently.
var texture: Texture2D = preload("res://sprite.png")
RenderingServer.canvas_item_add_texture_rect(ci_rid, Rect2(-texture.get_size() / 2, texture.get_size()), texture)
Server を使ったスレッド処理
- シーンツリーはスレッドセーフではありません。一方、Server API(RenderingServer、PhysicsServer)は、Project Settings で有効にすればスレッドセーフです。
- シーンチャンク(インスタンス化 + add_child)をワーカースレッド上で構築できますが、ライブツリーに追加するには必ず
add_child.call_deferred()を使う必要があります。 - GDScript の Dictionary/Array:スレッド間の読み取りと書き込みは安全ですが、サイズ変更(append、erase、resize)には
Mutexが必要です。 - 同じ
Resourceを複数のスレッドから同時に読み込むことは絶対に避けてください。読み込みスレッドは 1 つだけ使用します。
🧩 パート 7:エキスパート向けコードパターン
一般的なアーキテクチャおよびゲームプレイシステムのエキスパート向け実装です。
- コンポーネントレジストリ:辞書を使ったコンポーネントの一元的な取得です。
- 安全なシグナルハンドラー:解放済みオブジェクトへの参照によるクラッシュを防止します。
- 非同期リソースローダー:アセットをスレッドで読み込みます。
- ステートマシン遷移ガード:状態の変更を検証します。
- スレッドセーフなチャンクローダー:低レベルのサーバー API を構築します。
- 視野円錐の検出:内積とレイキャストを使ったエキスパート向け NPC の視覚処理です。
- 音響伝播システム:音響遮蔽のロジックです。
- ステルス時の隠蔽ロジック:全体的な可視性と隠蔽を管理します。
🔥 パート 8:Godot 4.x の注意点(ベテラン向け)
@exportのリソースはデフォルトで共有されます:複数のシーンインスタンスが、すべて同じResourceを共有します。_ready()でresource.duplicate()を使用するか、「Local to Scene」チェックボックスを有効にしてください。これは、Godot 4 で初心者から最も多く報告されているバグです。- シグナルの構文はエラーなく失敗します:
connect("signal_name", target, "method")(Godot 3 の構文)はコンパイルされますが、Godot 4 では何も行いません。signal_name.connect(callable)を使用する必要があります。 Tweenは Node ではなくなりました:create_tween()で作成され、作成元のノードのライフタイムに紐付けられます。そのノードが解放されると、Tween も破棄されます。永続的な Tween にはget_tree().create_tween()を使用してください。PhysicsBodyのレイヤーとマスク:collision_layerは「自分が何者か」を表します。collision_maskは「何を検出するか」を表します。両方に同じ値を設定すると、自身との衝突が発生するか、検出に失敗します。- 高頻度処理での
StringNameとString:StringName(&"key")はポインター比較を使用します(O(1))。Stringは文字比較を使用します(O(n))。_process内で辞書のキーを扱う場合は、必ずStringNameを使用してください。 @onreadyのタイミング:_init()の後に、_ready()の実行中に処理されます。コンストラクターの実行時にセットアップする必要がある場合は、_init()を使用してください。ツリーへのアクセスが必要な場合は、@onreadyまたは_ready()を使用してください。両者を混在させると、null が発生します。- サーバークエリによる停止:
_process内でRenderingServerまたはPhysicsServerの getter 関数を呼び出すと、パイプラインの同期フラッシュが発生します。これらのサーバーは非同期で動作するため、データを要求するとサーバーが追いつくまでパイプライン全体が停止します。 move_and_slide()の API 変更:衝突が発生したかどうかを示すboolを返します。Velocity は引数ではなく、プロパティになりました。move_and_slide()を呼び出す前に、velocity = dir * speedを設定してください。
📂 パート 9:モジュールディレクトリ(99 の設計図)
重要
現在のワークフローに必要なモジュールのみを読み込んでください。パート 2 の決定マトリックスを使って、たどるべきチェーンを判断してください。
アーキテクチャと基盤
基盤 | コンポジション | アプリのコンポジション | シグナル | オートロード | 状態 | リソース | テンプレート | アナリスト | 監査担当 | ビルダー
バージョンアップグレード(外部ハブ): godot-version-migration — 全履歴ルーター(旧バージョン、3→4 のブリッジ、4.0→4.7 の段階的移行)。ここには複製されていません。
GDScript とテスト
GDScript の習得 | テストパターン | デバッグ/プロファイリング | パフォーマンス最適化
2D Systems
2D Animation | 2D Physics | Tilemaps | Animation Player | Animation Tree | CharacterBody2D | Particles | Tweening | Shader Basics | Camera Systems
3D Systems
3D Lighting | 3D Materials | 3D World Building | Physics 3D | Navigation/Pathfinding | Procedural Generation | Raycasting
Gameplay Mechanics
Abilities | Combat | Dialogue | Economy | Inventory | Questing | RPG Stats | Turn System | Audio | Scene Transitions | Save/Load | Secrets | Collections | Waves | Harvesting | Time Trials | Revival | Monte Carlo Balancer
UI & UX
UI Containers | Rich Text | Theming | Input Handling | Seasonal Theming | Agent Vision
Connectivity & Platforms
Multiplayer | Server Logic | Export Builds | Desktop | Mobile | Web | Console | VR
Adaptation Guides
- Adapting Desktop -> Mobile
- Adapting Mobile -> Desktop
- Adapting Single -> Multiplayer
- Adapting 2D -> 3D
- Adapting 3D -> 2D
Genre Blueprints (Exhaustive)
Action RPG | Shooter | Shooter FPS | RTS | MOBA | Rogue-like | Survival | Open World | Metroidvania | Platformer | Fighting | Stealth | Sandbox | Horror | Puzzle | Racing | Rhythm | Sports | Battle Royale | Card Game | Visual Novel | Romance | Simulation | Tower Defense | Idle Clicker | Party | Educational
🐛 Part 10: Expert Diagnostic Patterns
「見えないノード」バグ
症状:ツリー内にノードが存在するのに、描画されません。
エキスパートによる診断手順:visible プロパティ → z_index → 親の CanvasLayer が誤ったレイヤーにある → modulate.a == 0 → カメラの near クリップ面の背後にある(3D) → SubViewport.render_target_update_mode が設定されていない → CanvasItem がどの CanvasLayer にも属していない(すべての背後に描画される)。
「入力が食われる」バグ
症状:クリックやキー入力が断続的に無視されます。
エキスパートによる診断:対象に重なっている別の Control ノードの mouse_filter = STOP。または、モーダルな PopupMenu が未処理の入力を消費している。あるいは、別のスクリプトの _unhandled_input() が get_viewport().set_input_as_handled() を呼び出している。
「物理演算のジッター」バグ
症状:キャラクターが接触面で振動します。
エキスパートによる診断:Safe Margin が大きすぎます。または、移動に _physics_process ではなく _process を使っている(補間の不一致)。あるいは、スポーン時にコリジョンシェイプが重なっている(互いに押し合い、ずれたままになる)。
「メモリーリーク」
症状:プレイ中に RAM が着実に増加します。
エキスパートによる診断:queue_free() を呼び出しても、Array/Dictionary 内に参照が保持されています。または、後処理をせずに CONNECT_REFERENCE_COUNTED でシグナルを接続しています。Profiler の「Objects」タブを使って、孤立したインスタンスを見つけてください。親を持たない Node インスタンスを検索してください。
「フレームスパイク」
症状:FPS は滑らかなのに、定期的に低下します。
エキスパートによる診断:GDScript の GC パス。または、大きなリソースを同期的に load() している。あるいは、NavigationServer が再ベイクしている。または、サーバー API のクエリが停止している(_process 内で RenderingServer からデータを要求している)。組み込みの Profiler でプロファイルし、関数単位のスパイクを探してください。
🚀 Part 11: クイックスタート — Unity(C#)から Godot(GDScript)へ
Unity エコシステムから移行するシニアエンジニア向けの考え方の転換です。
1. ノードと GameObject/コンポーネント
Unity では、GameObject は Component のコンテナです。Godot では、すべてが Node です。
- Unity:
GameObject+Transform+MeshFilter+Script。 - Godot:スクリプトをアタッチした
MeshInstance3Dノード(それ自体が Transform と Mesh です)。 - エキスパート向けの転換:ノードのコンポジションを使います。「Health Component」が必要なら、「Health」という名前の
NodeまたはArea3Dを子として追加します。ロジックのみのコンポーネントにはRefCountedを使い、メモリーを節約します。
2. シーンは入れ子にできる Prefab
Godot に「Prefab」がないのは、すべてのシーンが Prefab だからです。
- シーンを別のシーン内に、無限にインスタンス化できます。
- エキスパート向けの転換:再利用可能な各システム(Player、Enemy、UI Button)は、それぞれ独自の
.tscnファイルにします。これにより、「後順走査」(親より先に子の準備が完了する)が実現します。
3. シグナルとイベント/アクション
Godot の Signal システムは、オブザーバーパターンのネイティブ実装です。
- Unity:
event Action OnDeath;。 - Godot:
signal died。 - エキスパート向けの転換:シグナルは第一級の機能です。Inspector に表示され、動的に、またはエディターから接続できます。Unity のシングルトンマネージャーに相当するグローバルイベントには、「Signal Bus」パターン(Autoload)を使います。
4. クラス拡張としてのスクリプト
ノードにスクリプトをアタッチすると、そのスクリプトはノードそのものになります。
- Unity:
GetComponent<MyScript>()。 - Godot:スクリプトはノードのクラスを 継承します(例:
extends CharacterBody3D)。 - エキスパート向けの転換:型付き GDScript(
var x: int = 5)を使うと、コンパイル速度が向上し、エディターの補完機能も利用できます。型がコンパイル時に判明している場合、型付き GDScript は最適化されたオペコードを使用します。
5. メモリー管理:ガベージコレクションによる停止なし
「GC スパイク」が発生する場合がある Unity の C# とは異なり、GDScript は参照カウントを使います。
- エキスパート向けの転換:オブジェクトは参照されなくなった時点で解放されます。これによりパフォーマンスが予測可能になり、大規模な Unity プロジェクトでよく見られる断続的な「カクつき」を避けられます。
6. StringName とパフォーマンス
Unity ではパフォーマンスのために int や Enum を使います。Godot では StringName を使います。
- エキスパート向けの転換:辞書やシグナル検索で定数時間(O(1))のポインター比較を行うには、
&"name"を使います。