mcpField
S
scenario-godot-gameplay

scenario-godot-gameplay

github.com/scenario-labs開発1.2k158 回インストール1 日前に更新
リポジトリ

scenario-godot-gameplay について

Godot 4.7.2でナビゲーション、AI、戦闘、会話、プロシージャルコンテンツを扱う開発者向けのゲームプレイシステムスキルです。シーンに依存しない判断ロジック、データ駆動のルール、測定可能なテストとベンチマークを重視します。

主な機能

  1. 01ナビメッシュの構築と同期
  2. 02規模に応じた AI 方式の選択
  3. 03衝突、ヒットボックス、弾の設計
  4. 04セーブ検証とバージョン移行
  5. 05会話、クエスト、生成コンテンツのワークフロー

活用シーン

  1. 01敵集団のフレームコスト測定
  2. 02NavigationAgent3D の移動検証
  3. 03ヒット判定と高速弾のテスト
  4. 04セーブ、会話、ダンジョン生成の確認

MCP 対応

scenario-godot-expert の知識が前提です。入力には、このスキルが MCP サーバーまたはクライアントを提供するとの記載はありません。

インストール

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

$npx skills add https://github.com/scenario-labs/skills --skill scenario-godot-gameplay

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

同梱ファイル

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

references/
critique.md
expert-notes.md
gui-paths.md
procedures.md
sources.md
scripts/agentkit/gameplay/
arena.gd
brain.gd
bullet_pool.gd
combat_layers.gd
crowd_bench.gd
dungeon_gen.gd
enemy_body.gd
enemy_manager.gd
hero_enemy.gd
hit_log.gd
hitbox_3d.gd
hurtbox_3d.gd
nav_tools.gd
perception.gd
quest_rules.gd
save_io.gd
state_machine.gd
state.gd
scripts/agentkit/gameplay/inventory/
inventory.gd
item_stack.gd
item.gd
scripts/agentkit/gameplay/states/
attack.gd
chase.gd
patrol.gd
search.gd

詳細

Godot のゲームプレイシステム(ゲームプレイシステムプログラマー)

対象:Godot 4.7.2.stable、Jolt、macOS Apple Silicon。

エキスパートレベルでは、ゲームプレイシステムはシーンなしでテストできる小さな契約です。純粋な判定関数、データ用の Resources、検出用のグループを使います。そのコストは、ゲームが必要とするエージェント数で測定し、異論が出る前に把握します。基本方針は、ルールをデータとして構築し、--fixed-fps 60 でヘッドレス実行してから、キャプチャを 1 つ確認することです。ここで名前が挙がっている関連スキルが利用可能なスキルにない場合は、ユーザーにインストールを依頼してください (npx skills add scenario-labs/skills --skill <name>)。無人で進める場合は、ツールスキーマを確認して作業を続け、不足している点を明示してください。

必須の前提知識: scenario-godot-expert(チャンネル、gd_run、レビューの反復、4.7 の落とし穴)。

ステータス(2026-10-02): Godot 4.7.2 で、ライブテスト 16 件、ラウンド 2 のチェック(P15)、オフラインスイートが合格しています(references/procedures.md、tests/code/godot-gameplay/)。 Dialogue Manager 4.1.0、LimboAI 1.8.1、GUT 9.7.1 は、それぞれ独自のプロジェクトコピーで実行しました。

方針(エキスパートならではの差分)

  1. ベンチマークには必ず --fixed-fps 60 を使い、通常のヘッドレス実行は避けてください。 ヘッドレスの各フレームは 6.9 ms スリープし(low_processor_usage_mode_sleep_usec)、フレーム数よりも少ない物理ティック数で実行されます。 Performance.TIME_* モニターは約 1 秒に 1 回更新され、固定 fps では 0 を返します。フレームあたりの実時間ミリ秒を測定し、3 回の中央値を取り、同じ実行内の A/B ペアを比較してください(実測、P0)。
  2. ベースラインが考えるほど、群衆はボトルネックではありません。 NavigationAgent3D の回避を使う 200 体の CharacterBody3D の敵は、M5 Max でフレームあたり約 2 ms のコストです。コストはエージェント 800 体まで線形に増加し(7.3 ms)、200 体では回避自体のコストはノイズの範囲内です。max_neighbors や経路再計算間隔を調整しても、測定可能な改善はありませんでした。回避の利点は正確性です。回避なしでは重なり合うペアが 3108 組あったのに対し、回避ありでは 0 組でした(P2)。エージェントにノードが不要な場合に限り、サーバーエージェントと MultiMesh 1 つに切り替えてください(200 体で 0.26 ms)。
  3. 「マップ」ではなく、割り当てたメッシュの準備を待ってください。 ドキュメントには物理フレームを 1 回待つとあります。実測では、最初の同期に 3 フレームかかりました。再ベイク後、領域が古いポリゴンをまだ提供している間にマップ ID が変わりました。割り当て前に region_get_iteration_id を読み取り、その値が変わるまで待ってから、さらにマップ同期を 1 回行ってください(nav_tools.wait_applied、P1)。
  4. 静的コライダーのグループからベイクしてください。 新しい NavigationMesh は、領域の子からメッシュとコライダーの両方を解析するため、領域の下に NPC がいると 3 m の穴ができてしまいます(Bramwell 2W4JP48oZ8U 00:12:37、実測 P1)。
  5. 判断条件に応じて AI の段階を選んでください。 群衆には enum の脳を使い(Shaggy Dev oqFbZoA2lnU 00:00:32)、ヒーローやプレイヤーキャラクターにはノード FSM を使います。デザイナーが行動を作成する場合はビヘイビアツリーを使い(Chap.C E_FIy2dTkNc)、汎用アクションを多数組み合わせる場合に限り GOAP を使ってください(Wahaha Yes cm5Jxo31plw)。処理予算の目安:敵 40 体を 5 Hz で更新すると AI なしの場合と比べて 0.07 ms、全敵を毎ティック更新すると 0.45 ms かかります(P3)。
  6. ヒットボックスはマスク、食らい判定はレイヤーを設定し、1 回の攻撃につき HitLog を 1 つ使ってください(Queble cX-vzfmzjnE 00:06:36、00:12:55)。新しいエリアはレイヤー 1、マスク 1 で始まり、所有者自身にもヒットします(P4)。
  7. 高速弾はデータとして扱うか、CCD 付きのボディを使ってください。 continuous_cd がない場合、30 m/s でも 8 個中 6 個のボールが厚さ 0.1 m の壁を通り抜けました。Jolt は 500 m/s で上限に達します。1 つの MultiMesh で描画するレイ掃引データ弾のコストは、1000 発あたり 0.35 ms です。CCD 付きのプールされた RigidBody3D は 1.05 ms かかります(P5)。
  8. .tres の保存ファイルはコードです。 GDScript の sub_resource はロード時に実行されます(Godotneers 43BZsLZheA4 01:03:14、P6 で再現)。ロード前にスキャンし、CACHE_MODE_IGNORE でロードしてから、バージョンを確認して移行し、暗号化するのではなく値を検証してください(01:01:37)。

NavigationAgent3D の仕様(P15、実測)

ドキュメントの警告(NavigationAgents のページ)と Bramwell(2W4JP48oZ8U 00:14:14)。特に記載がない限り、4.7.2 で測定:

  • 回避には target_position が必要です。 回避を有効にして目標を設定しない場合、safe_velocity は 30 ティックの間 0 のままでした。目標を設定すると、3 m/s の速度がそのまま返されました。
  • 回避は経路探索に影響しません。 エージェントはナビメッシュや物理に関する知識を持たない円形のため、壁は引き続き経路から決まります(ドキュメント)。
  • 物理フレームごとに get_next_path_position() を 1 回呼び出し、経路の終端に到達した後は呼び出さないでください。 is_navigation_finished() が true になった後も最後のポイント(ここでは 0.93 m 先)を返すため、そこでステアリングを止めないとエージェントがじわじわ進みます。
  • path_desired_distance は、1 ティック分の移動距離と、床面から上にあるナビメッシュの浮動小数点誤差(約 0.3 m)を超える値にする必要があります。 25 m/s(1 ティックあたり 0.42 m)で 0.1 に設定した場合、1800 ティック中 1782 ティックでエージェントが逆走し、到着しませんでした。1.0 に設定すると 182 ティックで到着しました。0.1 に設定し、足元を床面の高さで測定した場合、最初の経路ポイントから進みませんでした。
  • デフォルト値: time_horizon_agents は 1.0、time_horizon_obstacles は 0.0 です。エージェントが早く減速しすぎる場合は、ホライズンを短くしてください。max_neighbors が少なすぎると、回避が無視されます(ドキュメントによるもの。ここでは未測定)。

最初に確認する項目

入力項目計画に影響する理由デフォルト
画面上のエージェント数ノードかサーバーエージェントか、思考予算200
ゲームプレイのフレーム予算すべてのベンチマークに適用される上限60 fps で 16.67 ms の 4 分の 1(4.2 ms)[追加]
2D または 3DNavigationAgent2D か 3D か、Area2D か 3D か3D
振る舞いの作成者コード(FSM、enum)かデザイナー(LimboAI)かコード
セーブデータの信頼性自分のファイルのみか、共有または MOD 対応か共有:スキャンまたは JSON
プラットフォーム入力デバイス、CPU 予算デスクトップ、キーボードとマウス、およびゲームパッド

ワークフロー

  1. プロジェクトをセットアップします。
    • gd_env.base_project("3d", P) で Base3D をクローンし、独自の application/config/name を設定します。そうしないと、クローン間で user:// が共有されます。gg.install(P) を実行します。
    • GATE:project.godot に 3d/physics_engine="Jolt Physics" と指定されたレンダラーがあり、gd_run.check_all(P) が解析エラー 0 件を報告すること。
  2. レベルのナビゲーション。
    • 静的ジオメトリはグループ nav_source に入れます。make_navmesh(parsed colliders, source group) でメッシュを構築し、次に bake、その後 wait_applied を実行します。
    • GATE:サンプル地点間の map_get_path が空でなく、障害物との交差が 0 件であること。ポリゴン数が 0 を超えていること。NPC のスポーン地点に穴がないこと(P1)。
  3. 移動と群衆。
    • 回避を有効にし、velocity_computed から move_and_slide を呼び出します。再経路探索は頻度を制限し、ステアリングは XZ のみで行います。
    • gg.crowd_bench(P, n) を 50、200、800 で実行し、次に gg.scaling_verdict を実行します。独自のベンチマークシーンでは、壁時計時間と並べて Performance.add_custom_monitor(&"ai/thinks", callable) と OBJECT_ORPHAN_NODE_COUNT をログに記録します(迷子になった Node.new() を 5 つ作成した後で 5 と表示されました。P15)。
    • GATE:重複ペアが 0 件であること。指数が 1.3 未満であること。目標数での中央値がゲームプレイ予算(gg.budget_share)内に収まること。孤立ノード数が一定であること。その後、群衆のウィンドウ表示キャプチャを開くこと。
  4. AI。
    • 脳をヒステリシス付きの decide(state, percept) として記述します。知覚処理はまず低コストのもの(距離、視野角、レイ 1 本、古いヒットの確認)から行います。マネージャーは約 5 Hz で思考し、ティックごとに上限を設けます。遠くの敵は思考頻度を下げます。ティアの判定には二乗距離を使います。ヘッドレスでは VisibleOnScreenNotifier3D.is_on_screen() が常に false になり、ベンチマークでは全員が遠距離ティアに入ってしまうためです(P15)。ノード FSM は主人公に使い、デザイナーが作成する場合は LimboAI を使います。
    • GATE:GUT の遷移表がすべて成功すること。主人公のシナリオ履歴が期待されるシーケンス(パトロール、追跡、攻撃、追跡、捜索、パトロール)と一致すること。思考コストの A/B を測定すること(P3、P9、P13)。
  5. 戦闘。
    • レイヤーに名前を付けます(World 1、Player 2、Enemy 3、PlayerHurtbox 5、EnemyHurtbox 6、Projectile 7)。ヒットボックスとハートボックスはキットから使用します。弾丸には bullet_pool を使い、跳ね返るオブジェクトには CCD を有効にした RigidBody3D を使います。
    • GATE:共有ログを使ったスイングで 1 ヒットを記録すること。同じ陣営なら 0 ヒットであること。トンネリングテストの最高速度で 8 件中 0 件になること。弾丸が独自のレイヤーにあること(P4、P5)。
  6. 永続化。
    • VERSION と migrate() を備えた型付きセーブ Resource を使い、save_io.save_resource_atomic と load_resource_checked を使用します。インベントリは所持者ごとに複製します。
    • GATE:ラウンドトリップ後に一致すること。古いフィクスチャがすべて読み込めること。不正なフィクスチャが拒否されること。キャッシュテストに合格すること(P6、P7)。
  7. 会話とクエスト。
    • Dialogue Manager 4.x の .dialogue ファイルを使います。クエストの順序は、納品、続行、依頼です。
    • GATE:インポート時にすべてのファイルがエラー 0 件でコンパイルされること。gg.check_dialogue_text で欠落したキューが見つからないこと。ヘッドレスでの進行により各分岐に到達し、状態が変更されること(P8)。
  8. 入力。
    • 物理キーのアクションを使い、ゲームプレイ入力は _unhandled_input で処理します。キー割り当ては ConfigFile に保存します。
    • GATE:合成プレスと just-pressed のテストに合格すること。STOP パネルが送信されたクリックを消費すること。再読み込み後もキー割り当てが保持されること(P10)。
  9. プロシージャルコンテンツ。
    • まずシード付きデータ(dungeon_gen)とバリデーターを用意し、次にジオメトリ、ベイク、経路の順で作成します。
    • GATE:1000 個のシードでバリデーターの失敗が 0 件であること。同じシードで同じハッシュが生成されること。開始地点からボスまでの経路が存在すること。最上部のキャプチャを開くこと(P11)。
  10. 仕上げ。
    • test_gameplay_live.py を実行し、証拠をコピーします。 references/critique.md に照らして自己評価します。

数値

値測定値比較条件
回避ありのボディ200体1フレームあたり1.98 ms、重なり0件M5 Max、固定60 fps、障害物14個の60 mアリーナ
回避ありのスケーリングエージェント50体:1.07 ms、200体:2.00 ms、800体:7.31 ms(エージェント1体あたり約9 µs)同上
サーバーエージェント200体とMultiMesh0.26 ms同上
思考コスト、敵200体予算0.40 ms、毎ティック0.78 ms、なし0.33 ms5 Hz、1ティックあたり40体
初回のナビメッシュ同期物理フレーム3回;リベイクは2〜16回後に適用非同期反復オン(デフォルト)
ベイク5 ms、ポリゴン180個60 mアリーナ、セル0.25
弾丸1000発データ0.35 ms;CCDありのRigidBody3Dは1.05 ms、CCDなしは1.00 ms(壁を45%の確率で通過)60 m/s、寿命2秒
Joltの速度上限500 m/sphysics/jolt_physics_3d/limits/max_linear_velocity
ダンジョン生成と検証中央値88 µs、有効1000件中1000件メイン経路14部屋、分岐4本
NavigationAgent3Dのデフォルト値radius 0.5、max_neighbors 10、neighbor_distance 50、max_speed 10、回避オフ4.7.2 ClassDB

品質ゲート

  • 測定可能な項目:群衆のメトリクス(重なり合うペア、ペア間の最小距離、スタック、床をすり抜けた数); scaling_verdict;budget_share;意図的に一度失敗させた後、GUT(gd_run.run_tests)が成功; check_allの解析エラーが0件;1000シードでのバリデーター;保存用フィクスチャ;会話のコンパイル。
  • ビジュアル:SubViewport内のウィンドウキャプチャ(160x90ウィンドウ)をgd_review.image_checksで確認し、 1600 px以下のコンタクトシートを開いて目視します。プレイヤーの周囲で群衆の間隔が保たれていること、 障害物の中にエージェントがいないこと、スタートからボスまでのダンジョン経路を確認します。

よくある間違い

間違いどのように現れるか修正
通常のヘッドレスモードでベンチマークを行う負荷にかかわらず「フレーム」あたり約 7 ms--fixed-fps 60
メッシュを割り当てた直後にパスを問い合わせるパスが空で、エージェントがスポーン地点で動かないwait_applied
NPC が内部にいる状態でデフォルトのベイク(ルートから Both)を行うスポーン地点の周囲に穴ができるグループと静的コライダー
「速度を上げるために」回避をオフにする3108 組が重なり、敵が融合する回避をオンにする:200 体でも負荷はわずかです
敵ごとに 1 つの Area3D を使って視界を判定するブロードフェーズの処理が増える距離、円錐、レイ 1 本 [追加]
テレポートしたボディへのレイ判定を信頼する敵が壁越しに「見える」ヒット位置が古くないか距離を確認する
ヒットエリアのレイヤーとマスクを 1 にする自分自身にヒットし、ダメージが 3 倍になるマスクかレイヤーのどちらか一方のみを使う;HitLog
area_entered 内で monitoring = false にする「in/out シグナル中は関数がブロックされます」set_deferred
デフォルトレイヤーに弾丸ボディを配置する1000 個のボディが衝突し、フレームあたり 110~170 ms かかる専用レイヤーを使い、マスクをワールドとターゲットに設定する
高速ボディで CCD を使わない弾丸が 30 m/s で壁を通り抜けるcontinuous_cd またはレイでスイープしたデータ
ダウンロードした .tres を読み込むスクリプトが実行されるload_resource_checked または from_native を使った JSON
再保存後にデフォルトの load() を使う古い値が戻るCACHE_MODE_IGNORE
Array[String] に save.items = [...] を使う実行時エラー、または set() 経由で何も起きないitems.assign([...])
各宝箱でインベントリの .tres をプリロードするある宝箱の戦利品がすべての宝箱に表示される各所持者ごとに duplicate(true)
Selector の最初の子が常に成功する攻撃ブランチが実行されない(Chap.C E_FIy2dTkNc 00:17:27)最初にゲートを置く、BTProbability または AlwaysFail を使う
アクションに論理キーコードを使うAZERTY で WASD が動作しない物理キーコードを使う
物理補間がオンの状態でテレポートするジャンプ中、ボディが滑るように描画される [追加、未測定]移動後に reset_physics_interpolation() を呼び出す(このメソッドは存在します)
オートロードに queue_free() を使う4.7.2 ではクラッシュしませんが、以降のアクセスはすべて「以前に解放済み」というエラーになり、読み取り値は 0 になります(ドキュメントではクラッシュすると記載)解放せず、代わりに状態をリセットする

引き継ぎ

  • 受け取るもの: プロジェクトのレイアウト、オートロードのポリシー、保存モデル(scenario-godot-architecture)、レベルと、nav_source 内の静的コライダーおよびコントローラー(scenario-godot-3d-world)、TileMapLayer のナビゲーション(scenario-godot-2d)、これら2つを通じた scenario-* スキルのアート。
  • 引き渡すもの: state_changed を scenario-godot-animation へ、再生イベントと優先度を scenario-godot-audio へ(ミックス、バス、減衰、リスナーはそちらで扱います)、インベントリーシグナル、会話バルーン、再割り当てメニューを scenario-godot-ui へ、ヒット位置と法線を scenario-godot-vfx へ、Resources と純粋な decide() を scenario-godot-multiplayer へ、ベンチマーク JSON を scenario-godot-performance-export へ。

Godot 4.7 に関する注意

  • ナビゲーションクラスは 4.7.2 では実験的です。リージョンとマップの反復処理はデフォルトで非同期のため、同期には数フレームかかります。region_bake_navigation_mesh は非推奨です。parse_source_geometry_data と bake_from_source_geometry_data(または非同期版)を使用してください。region_get_iteration_id は存在します。query_path(params, result, callback) はコールバックを戻る前に実行しました(P15)。処理をフレーム外に移すものではありません。
  • MultiMesh は1つのボックスとしてカリングされます。custom_aabb(存在します)を設定し、大規模な群衆はエリアごとに分割してください。
  • NavigationMesh の geometry_parsed_geometry_type のデフォルトは Both(2)です。セルサイズと高さのデフォルトは 0.25 です。
  • 4.6 以降、新規プロジェクトでは Jolt が 3D のデフォルトですが、エージェントが作成した project.godot は、Jolt を設定しない限り GodotPhysics3D を使用します(リードの差分)。
  • duplicate(true) がコピーするのはファイル内サブリソースのみです(4.5 以降)。 duplicate_deep(DEEP_DUPLICATE_ALL) は外部サブリソースもコピーします。
  • JSON.from_native と to_native が存在します。通常の JSON 数値は float として返されます。
  • InputEventKey.as_text() は "W - Physical" と表示します。新しい InputEventKey の device は 16 です (DEVICE_ID_KEYBOARD)。
  • アドオン:Dialogue Manager は 4.1.0 で、タイトルはキューです。DM3 の動画は1バージョン古いものです。 LimboAI 1.8.1 には、4.7.2 で動作する「4.6」GDExtension ビルドが同梱されています。

参考資料

  • references/procedures.md(コードと実行結果を含む P0~P15)、 expert-notes.md(専門家による判断、タイムスタンプ)、critique.md (評価基準)、gui-paths.md(エディターのメニュー)、 sources.md(動画、ドキュメント、アドオンのハッシュ)。
  • scripts/gd_gameplay.py:インストール、run_job、crowd_bench、 scaling_verdict、budget_share、会話のリント、セーブのスキャン。
  • scripts/agentkit/gameplay/:nav_tools、arena、crowd_bench、 enemy_body、ステートマシンとステート、brain、perception、enemy_manager、hitbox、hurtbox、hit_log、 combat_layers、bullet_pool、save_io、inventory、quest_rules、dungeon_gen、voice_pool。

コメント