mcpField
G
godot-game-loop-waves

godot-game-loop-waves

github.com/thedivergentai開発925269 回インストール3 週間前に更新
リポジトリ

godot-game-loop-waves について

アリーナ、サバイバル、タワーディフェンスの遭遇戦を作る Godot 開発者向けに、データ駆動の敵ウェーブシステムを扱うスキルです。マネージャー、スポナー、Resource のパターンを備え、シーンノードからサーバー側の群体処理まで対応します。

主な機能

  1. 01Resource によるウェーブ編成の保存
  2. 02ウェーブの時間制御とクリア判定
  3. 03Marker と重みによるスポーン
  4. 04オブジェクトプールと遅延ノード生成
  5. 05MultiMesh とサーバー RID の群体パターン

活用シーン

  1. 01ストーリー向けの直線的なウェーブ作成
  2. 02サバイバルモードのエンドレスウェーブ生成
  3. 03指定地点への重み付き敵スポーン
  4. 04高密度群体を個別シーンノードの制約外へ拡張

インストール

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

$npx skills add https://github.com/thedivergentai/game-design-agentic-skills --skill godot-game-loop-waves

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

同梱ファイル

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

references/
migration-notes.md
wave-expert-patterns.md
scripts/
wave_loop_patterns.gd
wave_manager.gd
wave_resource.gd
wave_spawner.gd
wave_weighted_spawner.gd

詳細

アーキテクチャ設計: 「Wave-State」パターン

マスター実装では、ウェーブをデータ駆動型の遷移として扱います。スポーン数をハードコードするのではなく、WaveResource を使用して「エンカウント」を定義し、WaveManager が順番に処理します。

主な役割
  • マネージャー:タイムラインを統括します。ウェーブ間の遅延を処理し、「勝利」条件(敵がすべて倒されること)を追跡します。
  • スポナー:敵が出現する位置の空間情報を提供する、疎結合のノードです(wave_spawner.gd / wave_weighted_spawner.gd)。
  • リソース:コードに触れずにデザイナーがゲームのバランスを調整できる、不変のデータコンテナーです。

密度の判断ツリー(コーディング前に規模を選択)

実行中の密度アプローチ必須の読み込み
SceneTree の敵が約 80 体未満ノードマネージャー + Marker スポナーwave_manager.gd、wave_spawner.gd、wave_resource.gd
群れのビジュアル/数百体MultiMesh + 重み付け構成wave_loop_patterns.gd(MultiMesh / 非同期パス)、wave_weighted_spawner.gd
約 10,000 体PhysicsServer / NavigationServer の RID(モブごとの Node なし)wave_loop_patterns.gd のサーバー RID パターン。wave_manager のノードスポーンでスケールさせないでください

wave_manager.gd は SceneTree の標準的な方法です(add_child を遅延実行し、グループ/シグナルでクリア数を追跡、オプションでプールを使用)。プロトタイプから中規模までを対象とします。RID 群れの場合は、数千のノードをインスタンス化せず、wave_loop_patterns.gd に従ってください。

構成の標準的な手順

  1. wave_resource.gd の構成テーブルを作成します。
  2. wave_spawner.gd の Marker を配置します(バリエーションの重み付けが必要な場合は wave_weighted_spawner.gd を使用します)。
  3. wave_manager.gd の spawner にその Marker を指定します。マネージャーはスポーンを遅延実行し、&"enemies" グループとシグナルを使ってクリアを判定します。
  4. 固定数を重み付けに置き換える場合は、構成ループから WaveWeightedSpawner.spawn_enemy() を呼び出します(または、マネージャーの spawner に重み付けノードを設定します)。

エキスパート向けコードパターン

1. 非同期ウェーブトリガー

カスタムタイムラインを作成する前に必ず読むこと。wave_manager.gd の await タイマーを使用します。スポーンには call_deferred(&"add_child", …) を使い、シグナル/グループでクリアを判定します(get_children() による走査は使用しません)。

2. 構成ベースのスポーン

wave_resource.gd でバリエーションを定義し、wave_spawner.gd / wave_weighted_spawner.gd でユニットを配置します。マネージャー内にシーンのパスをハードコードしないでください。

マスター判断マトリクス:進行方式

パターン最適な用途ロジック
リニアストーリーミッション手作業で作成した WaveResource のリスト。
エンドレスサバイバルモード乗数計算を用いてコードで生成する WaveResource。
トリガー式RPG のエンカウントプレイヤーが Area3D に入ったときのみウェーブを開始します。

絶対に避けること

  • 敵をすべて探すために get_children() を繰り返し呼び出さないでください — これは非常に低速です。敵は必ず「enemies」グループに追加し、効率的にアクセスするには get_tree().get_nodes_in_group(&"enemies") を使用してください。
  • 数百体の敵に対して、常に instantiate() と queue_free() を繰り返さないでください — これによりガベージコレクションによるカクつきが発生します。オブジェクトプールを使用して、既存の敵インスタンスを再利用してください。
  • 群れを表現するために、個別の MeshInstance3D ノードを何千個も生成しないでください — ドローコールが大幅に増加します。MultiMeshInstance3D を使用して、何千ものメッシュを単一の GPU 呼び出しにまとめてください。
  • 何百ものエージェントの経路探索をメインスレッドで計算しないでください — ゲームがフリーズします。ナビゲーション領域で use_async_iterations を有効にするか、NavigationServer3D.query_path() を使用してください。
  • 子ノードを追加する前に is_inside_tree() を確認し忘れないでください — スポナーが削除待ち状態の場合、子ノードの追加時にクラッシュします。スポナーが引き続きツリー内でアクティブであることを必ず確認してください。
  • プリロードしたリソース(stats.tres など)を、生成したモブに直接割り当てないでください — すべてのモブで同じヘルス値やステータスが共有されます。各モブに固有のデータを持たせるため、必ず base_stats.duplicate_deep() を呼び出してください。
  • 頻繁に呼び出すグループ処理に標準の文字列を使用しないでください — ハッシュ性能を最適化し、不要な文字列の割り当てを避けるため、必ず StringName(&"enemies"、&"take_damage")を使用してください。
  • 物理コールバック内でエンティティを同期的に直接生成しないでください — 物理処理中にノードをインスタンス化すると、物理状態が破損する可能性があります。必ず call_deferred(&"add_child", enemy) を使用してください。
  • 死亡した敵の CollisionShapes を有効なままにしないでください — 死体がタワーやナビゲーションを妨げます。死亡時にすぐ set_deferred("disabled", true) を使用してください。
  • 複雑な Object 型を MultiplayerSynchronizer で同期しないでください — サポートされているのはプリミティブ型のみです。複雑なデータを同期する場合は、UID または ID を同期し、クライアント側でデータをローカル検索してください。
  • プレイヤーへのフィードバックなしにウェーブを自動開始しないでください — プレイヤーが自分で判断できるよう、UI のカウントダウン、視覚的な「ウェーブ接近中」エフェクト、または開始ボタンを必ず用意してください。
  • スポーン位置を (0,0,0) にハードコードしないでください — エディターで Marker3D ノードを使用すれば、コードを調べずにスポーンポイントを視覚的に調整できます。
  • 毎フレーム、子ノードを数えてウェーブの完了を確認しないでください — 処理コストが高すぎます。ローカルカウンターを維持するか、シグナルベースのシステムを使用して、アクティブな敵の数を追跡してください。
  • すべてのエンティティタイプに同じナビゲーションマップを使用しないでください — 飛行する敵と歩行する敵がいる場合は、経路探索の問題を防ぐため、別々のナビゲーションマップを使用してください。
  • スポナーのコリジョンシェイプを不均等にスケーリングしないでください — コリジョン検出の計算が破綻します。代わりにシェイプリソースのプロパティを調整してください。

利用可能なスクリプト

必須:対応するパターンを実装する前に、適切なスクリプトを読んでください。

wave_loop_patterns.gd

エキスパート向けパターン 10 種:MultiMesh による群れ、非同期経路探索、バックグラウンドでのプリロード、サーバー側の物理モブ。

wave_manager.gd

タイムラインとウェーブ間の遅延を管理し、グループ数とシグナルを使って全滅を追跡します。call_deferred による add_child を使用し、use_pool / recycle_enemy によるプールは任意です。

wave_resource.gd

ウェーブの構成と難易度設定のデータコンテナー。

wave_spawner.gd

Marker3D の空間ポータル — オプションで半径方向のジッターを加えた get_spawn_position() を使用します。WaveManager.spawner として接続してください。

wave_weighted_spawner.gd

Marker で重み付きランダム選択によって敵を選びます。固定数ではなく、確率に基づいて構成にバリエーションを持たせる場合に使用してください。


エキスパート向けウェーブパターン

1. 群れのオクルージョンカリング

何百もの敵がいる場合のパフォーマンスを最適化するには、オクルージョンカリングを有効にします。

  • 設定:アリーナに OccluderInstance3D を追加してベイクします。
  • 結果:壁や柱の背後に完全に隠れた敵は GPU で処理されなくなり、FPS が大幅に向上します。
2. ウェーブ UI アーキテクチャ

CanvasLayer とシグナルを使用して、ウェーブデータと UI を分離します。

  • ウェーブカウンター:現在のウェーブ数と合計ウェーブ数を表示します。
  • ヘルスバー:ボスには CanvasLayer 上の TextureProgressBar を使用するか、個々の敵のヘルスバーにはビューポートテクスチャを使った Sprite3D を使用します。

必要に応じて参照する専門知識

LLM の無知に関するルール: 一般的なエージェントが読む前に知らない情報は、参照資料を読み込んでください。専門的な差分は決して削除しないでください。

参考資料

段階的な情報開示:特定の API を調査するときにのみ公式ドキュメントのリンクを開き、関連分野へ案内するときにのみ関連スキルを読み込んでください。関連情報をすべて事前に読み込まないでください。

公式ドキュメント
  • リソース — WaveResource の構成と遅延をデザイナーが編集できるようにし、マネージャーにスポーンテーブルをハードコードせずに済みます。
  • ノードとシーンインスタンス — PackedScene.instantiate() と add_child / call_deferred を組み合わせる方法は、ウェーブの敵を安全にスポーンする手段です。
  • グループ — 毎フレーム子ノードを走査する代わりに、StringName グループと get_node_count_in_group を使って生存中の敵を追跡します。
  • アイドル処理と物理処理 — タイミング制御にはタイマー/await を使用します。遅延処理せずに物理コールバック中にインスタンス化しないでください。
  • SceneTreeTimer — ウェーブ開始前の遅延やスポーン間隔には create_timer を使用し、終了しない _process のカウントダウンは避けます。
  • シグナルの使用 — wave_started / wave_cleared / all_waves_complete により、UI、オーディオ、戦闘処理をマネージャーのタイムラインから分離できます。
  • バックグラウンド読み込み — ボスや負荷の高いウェーブには ResourceLoader.load_threaded_request を使用し、最初のスポーン時に処理が停止するのを防ぎます。
  • 乱数生成 — RandomNumberGenerator.rand_weighted を使用して、重み付きの構成とスポーンの揺らぎを設定します。
  • MultiMesh の使用 — ミニオンが何百体もいてドローコールが急増する場合は、群れのビジュアルをまとめて描画します。
  • オクルージョンカリング — カメラ外のアリーナの敵を非表示にし、密集したウェーブでも GPU の負荷を抑えます。
  • ナビゲーションの概要(3D) — 非同期のエージェント経路と、飛行ユニット用・歩行ユニット用に分けたマップを使用します。
  • サーバーの使用 — SceneTree のノードでは拡張性が足りない場合は、PhysicsServer3D/NavigationServer3D の RID で群れを管理します。
関連スキル
前提条件
  • godot-project-foundations — WaveManager をアリーナに組み込む前に、シーンツリー、エクスポート、グループについて確認します。
  • godot-gdscript-mastery — 非同期のウェーブトリガーが依存する、型付き await、シグナル、call_deferred のパターンを扱います。
  • godot-resource-data-patterns — Resource/@export による構成と duplicate_deep を扱い、スポーンした敵同士でステータスが共有されないようにします。
補完
  • godot-signal-architecture — ウェーブ/UI/戦闘シグナルの所有権を扱い、カウントダウンとクリアイベントでリークが発生しないようにします。
  • godot-combat-system — ウェーブの敵ユニットが倒れた際にアクティブな敵のカウンターを減らす、ヒットボックス、死亡、ダメージを扱います。
  • godot-navigation-pathfinding — 数百体のウェーブエージェントの経路探索に使う、NavigationServer のクエリ、回避マスク、マップを扱います。
  • godot-scene-management — ウェーブで PackedScenes が頻繁に入れ替わる際の、プール、スレッド化された読み込み、安全な追加/削除を扱います。
  • godot-performance-optimization — 群れの密度予算に向けた MultiMesh、オクルージョン、サーバー側ボディを扱います。
  • godot-monte-carlo-balancer — エンドレスモードのリリース前に、スタイルマトリックスを使ってスポーン数、出現率、難易度カーブをサンプリングします。
下流/利用側
  • godot-genre-tower-defense — WaveResource のシーケンスとクリア勝利条件で駆動する、レーン/ポータルウェーブを扱います。
  • godot-genre-shooter — ウェーブ制シューターのアリーナ/大群のスポーンペースと敵の多様性を扱います。
  • godot-genre-survival — 同じマネージャーループを基盤とする、エンドレスの倍率ウェーブと圧力の上昇を扱います。
マスター
  • godot-master — スキル横断での検索に使う、ライブラリのルーターとミラー化されたモジュールエントリです。

コメント