mcpField
S
scenario-unity-gameplay

scenario-unity-gameplay

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

scenario-unity-gameplay について

Unity 6.3 のゲームプレイシステム開発向けスキルで、物理、ナビゲーション、AI、弾丸、オーディオ、ネットワークを扱います。状態の書き込み元を一つにし、測定とテストで挙動や性能を検証します。

主な機能

  1. 01物理と衝突検出の設定
  2. 02NavMesh ナビゲーションの構築と検証
  3. 03AI の行動と更新スケジュールの構成
  4. 04弾丸クエリとプーリングの実装
  5. 05ゲームオーディオのルーティングと上限制御
  6. 06拡張性とネットワーク要件の評価

活用シーン

  1. 01高速弾丸のコライダー貫通をテスト
  2. 02ドアを通る NPC の経路と状態遷移を確認
  3. 03多数のエージェントによるフレーム時間を測定
  4. 04マルチプレイヤーの予測と権限モデルを検討

インストール

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

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

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

同梱ファイル

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

references/
critique.md
expert-notes.md
gui-paths.md
procedures.md
sources.md
scripts/AgentKit/Gameplay/
GameplayAudio.cs
GameplayAudit.cs
GameplayScenes.cs
GameplaySetup.cs
scripts/Runtime/
AgentKit.Gameplay.Runtime.asmdef
AgentLocomotionSync.cs
AIBudget.cs
AIBudgetMeter.cs
CombatAudio.cs
EnemyBrain.cs
EnemyDirector.cs
FloatingCapsule.cs
Health.cs
JobStallProbe.cs
JobWaitMeter.cs
LineOfSightBatch.cs
MixerControl.cs
NetSim.cs
Perception.cs
PerceptionBenchmark.cs
Projectile.cs
ProjectilePool.cs
ProximityBenchmark.cs
Queries.cs
SoundEmitter.cs

詳細

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

Unity 6.3 でのエキスパートレベルのゲームプレイ開発では、各システムが状態の唯一の所有者を持ち、誰かが選んだ頻度で実行され、測定によって実証されます。フレームあたりのステップ数、実際の速度でのトンネリングテスト、実際のドアを通る経路クエリ、時間を計測した遷移、実際のエージェント数での最悪フレームなどです。このスキルでは、6.3 のマニュアル、AI Navigation 2、NGO、リリース済みゲーム(Survival Kids、Very Very Valet)から得られるエキスパート向けの知見を扱い、ジョブとテストは 6000.3.21f1 で実行します。ここに記載された関連スキルが利用可能なスキルにない場合は、ユーザーにインストールを依頼してください (npx skills add scenario-labs/skills --skill <name>)。無人で進める場合は、ツールのスキーマに基づいて作業を続け、不足している情報を明示してください。

必須の事前知識: scenario-unity-expert(チャネル、レビューの反復プロセス、6.3 の落とし穴)。インストール: ut_gameplay.install(P)(コア AgentKit + AgentKit.Gameplay ジョブ + AgentKit.Gameplay.Runtime アセンブリ)。

立場(専門家としての差分)

  1. コライダーより先に固定更新の頻度を調整します。 物理演算のコストは、フレームごとの固定ステップ数に応じて増加します。「呼び出し回数が10近くになる場合は問題の兆候かもしれません」(6.3 Manual)。50 Hzで測定したところ、400 msのフレームではデフォルトのMaximum Allowed Timestepである0.333 sにより、追いつくためのステップが17回実行されました。上限を0.1 sにすると5ステップになり、ワールドの速度は0.25倍になりました。上限を設けると、負荷の雪だるま式の増大を抑えられますが、スローモーションになります。
  2. 観測されたトンネリングに対してオブジェクトごとにCCDを段階的に適用し、その後は剛体に任せます。 Discrete、Continuous Speculative(「多くの場合、最適な選択肢」)、Continuous、最後にContinuous Dynamicの順です(6.3 Manual)。測定では、どのCCDモードでも静的な厚さ5 cmの壁に対して300 m/sまで防げました。一方、薄い動的なプレートに対しては、Continuousでは50 m/s、Continuous Dynamicでは100 m/sからトンネリングが発生しました。飛翔体自体の形状をスイープする(SweepProjectile)と、300 m/sの弾を止められました。また、半径0.25 mの球体スイープは、中心を通るレイでは見逃した柱に当たりました。キャスト形状はコライダーに合わせてください(LlamAcademy, fJyi7l2tWKo [00:09:54])。
  3. 状態の各部分には、書き込み元を1つだけ設けます。 エージェントと非キネマティックなRigidbodyを併用すると競合が起こります。エージェントと有効なObstacleを併用するとエージェントが自分自身を回避します。エージェントとアニメーションの制御は一方向に流す必要があります(AI Navigation manual)。測定では、実行中のTransformAccessArrayジョブが保持するTransformにメインスレッドから書き込むと、ジョブ全体が終わるまで待機しました(ジョブ外では0.003 msなのに対し、22~38 ms)(Survival Kids, ZkvK0mX-id4 [00:26:42])。
  4. NavMeshはクエリで検証し、アセットとして保存します。 エージェントの種類ごとにNavMesh Surfaceを1つ用意します(SMWxCpLvrcc [00:06:35])。ボクセルサイズはエージェントの半径の3倍、屋内では4~6倍にします(manual)。測定では、エージェントの直径の1.2倍のドアは、半径あたり3ボクセルでは通れず、6ボクセルでは通れました。HeightMeshなしの階段では踏み面から0.17~0.26 mずれましたが、HeightMeshを使うとずれはありませんでした。Carveの結果がクエリに反映されるのは1フレーム後でした。
  5. 最も軽量なAI構造を選び、1か所から更新します。 振る舞いが1~2個なら構造化せず、遷移が少ないうちはFSMを使います(遷移が増えるとNの2乗に近づきます)。優先順位が必要ならビヘイビアツリー、目標を連鎖させるならGOAPを使います(LlamAcademy, CZvfuNfdc1M [00:07:13])。ブレインはディレクターから更新し、異なる種類のオブジェクトは1つのUpdateHubに登録して、種類ごとにソートします(Survival Kids:コードは同じで、平均-12%、最悪のフレームは2.18 msから1.43 msに短縮 [00:23:59]。ここで1,000個および10,000個のオブジェクトを測定したところ、平均で7~14%低下)。
  6. DOTSは包括的な枠組みです。採用する前に測定してください。 更新順序を整え、Transformを小さな読み取り専用ジョブでコピーしながら、NativeArrays上でBurstジョブを実行します。次に、メインスレッドのジョブ待ち時間をBurstの.Run()で埋めます(測定では、21~26 msのうち6~10 msを削減)。その後で初めてEntitiesを導入します(Survival Kids [00:08:49]、[00:39:15])。CPU主導の大規模な処理数、競技性の高いネットコード、または決定論性が必要な場合は、早い段階でECSを採用するか判断してください(Turbo Makes Games, Bz24Jp30nkM [00:21:58])。
  7. NGO 2.xには、完全なクライアント側予測とリコンシリエーション(ロールバックと再実行のループ)がなく、サーバー巻き戻しにも対応していません。 クライアント予測(AnticipatedNetworkVariable、AnticipatedNetworkTransform、OnReanticipate)は、手動で予測を実装するための基盤です(NGO 2.7 manual。同じ説明は同梱の2.13.0ドキュメントにもあります)。デフォルトではサーバーが権限を持ち、IsServerよりHasAuthorityを使います。「後から参加するクライアントに必要か?」を基準に、NetworkVariableとRPCのどちらを使うか判断します。
  8. すべてのサウンドをミキサーグループ経由にし、距離による処理はソース側で行います。 出力がNone(デフォルト)の場合、ミキサーを経由しません。減衰と距離によるローパス処理は、グループに送られる前にAudioSourceで行われます(manual)。測定では、対数ロールオフによりMax Distanceを超えても減衰が続きました(40 mで1/d)。スナップショットパラメーターのスライダーは、ClearFloatを呼び出すまでそのグループのムードを固定しました。また、上限なしのループする武器音を60個鳴らすと、デフォルトの優先度では小音量のBGMがかき消されました。頻繁に鳴るサウンドは10~15個に制限し、最も古いものを奪い合いで置き換えてください(git-amend, BgpqoRFCNOs [00:17:56])。

最初に確認すること

一度だけ確認します。ジャンルと対戦プレイかどうか、プラットフォームとフレーム予算(デフォルトはデスクトップで 16.67 ms、モバイルでは x 0.65、scenario-unity-mobile)、ピーク時のエージェント数、弾数、サウンド数、プレイヤーが物理演算で押す側・押される側になるか、アニメーション駆動かエージェント駆動かの移動方式、マルチプレイヤー(プレイヤー数、ホスト離脱、ロールバックまたは被弾巻き戻し)、既存のレイヤー、ミキサー、シーン、2D か 3D か(scenario-unity-2d)。デフォルトは、3D URP、50 Hz、レイヤーは Environment 8、Player 9、Enemy 10、Projectile 11(Projectile x Projectile と Enemy x Enemy はオフ)、Humanoid エージェントタイプ(半径 0.5)、10 Hz のコード FSM 敵、ミキサー 1 つ(Master > Music、SFX、UI)です。

ワークフロー

  1. プロジェクトの物理設定。 GameplaySetup.ConfigurePhysics(レイヤー、Physics.IgnoreLayerCollision、SerializedObject を介した Time と Physics の設定)。ゲート:別プロセスで ReadPhysicsSettings を実行し、マトリックス、0.02 s、上限値が表示されること(実測:Time.* セッターと SaveAssets では設定が永続化されませんでした)。
  2. ナビゲーション。 GameplayScenes.BuildNavArena のパターンに従います。サーフェスの layerMask = Environment、ボクセル = 半径/3、BuildNavMesh() を実行してから、Inspector の Bake ボタンと同様に AssetDatabase.CreateAsset(surface.navMeshData, ...) を実行し、保存します。エージェントが階段を使う場合は Height Mesh をビルドします。植生には NavMesh Modifier の Remove Object を設定します。収集対象は、範囲を限定するために Volume または Modifier-only にします。ゲート:すべてのドアを通る CalculatePath が PathComplete を返すこと、スポーン地点と踏み面で SamplePosition が機能すること、開き直した後もデータが存在すること、シーンがテキスト形式のままであること、オーバーレイのキャプチャを開けること。
  3. キャラクターと移動。 物理演算との相互作用を設計に含めない場合は CharacterController(skin >= max(0.01, 半径の 10%)を使い、含める場合は FloatingCapsule を使います。AgentLocomotionSync でエージェント移動を行います。エージェント駆動(ルートモーションをオフにし、deltaPosition から速度を取得)またはアニメーション駆動(updatePosition = false、半径を超えた場合に 0.9 の引き戻し)を選びます。ゲート:cc.* の監査エラーが 0 件であること、ControllerTests の数値を確認すること、ドリフトテストに合格すること。
  4. AI。 EnemyBrain と EnemyDirector を使います。負荷の低い知覚処理から始め、次にオクルーダーマスクを使った 1 回のレイを QueryTriggerInteraction.Ignore で実行します。規模が大きい場合は LineOfSightBatch(QueryParameters を使った RaycastCommand)でバッチ処理します。死亡時は障害物を有効にする前にエージェントをオフにします。ゲート:EnemyLoopTests の時間を Patrol > Chase > Attack > Search > Patrol > Dead の順で測定します。
  5. 弾、クエリ、プール。 剛体の弾は、実際の速度と厚さでトンネリングテストを行い、CCD を確認します。高速または太い弾には SweepProjectile を使います。Queries を介して NonAlloc を使います(件数分ループし、バッファーが満杯ならフラグを立て、順序が重要な場合はソートします)。Transform で移動させたコライダーをクエリする前に Physics.SyncTransforms() を呼び出します。collectionCheck を有効にした ProjectilePool を使います。ゲート:実際の数値で CcdLadderMeasured と QueryTests に合格し、CountActive が 0 に戻ること。
  6. オーディオ。 GameplayAudio.CreateCombatMixer またはテンプレートからミキサーを作成し、すべてのソースをルーティングします。CombatAudio の同時発音数に上限を設定します。スライダーは、どのスナップショットからも駆動されないパラメーターに接続します。音楽の優先度は 0 にし、AudioLowPassFilter のカーブで距離に応じたこもりを設定します。ゲート:AudioTests と AudioSpatialTests(スパム再生中の音楽帯域)に合格し、audio.* のエラーが 0 件であること。上限を 30、15、5 として負荷をかけながら人間が試聴し、十分な音の厚みがある最小値を選びます(BgpqoRFCNOs [00:17:23])。
  7. スケール。 実際の数で BuildCrowdScene を実行し、AgentProfile.PlayModeTimings、AIBudgetMeter、JobWaitMeter を確認します。DOTS の導入を提案する前に、PerceptionBenchmark、ProximityBenchmark、dots_gate、proximity_gate を実行します。ゲート:p95 と最悪フレームの両方を確認します。エディターでの数値を、Profile Analyzer とネイティブデバイスプロファイラーを使って開発用プレイヤーで検証します(Survival Kids [00:10:27]; scenario-unity-performance)。
  8. ネットコードの適用範囲(マルチプレイヤーのみ)。 ut_gameplay.netcode_fit、オーソリティとティックのシートを確認し、NGO を接続する前に Edit Mode で NetSim を使って予測を検証します。ゲート:リコンシリエーションのテストがすべて成功し、手作業で構築する必要があるものが設計に明記されていること。

数値

値相対項目出典
0.02 s(50 Hz);60 fps のフレームごとに 1 ステップなら 1/60固定タイムステップマニュアル;pTz3LMQpvfA [00:01:22]
デフォルトで 0.333 s(50 Hz で約 16 ステップ);0.1 s = 5 ステップ最大許容タイムステップ実測
1 フレームあたり約 10 回の呼び出しで問題Physics.Processing 呼び出しマニュアル
約(厚さ + 2r + 0.02)/ dt を超えるとディスクリート方式ですり抜ける50 Hzdiscrete_tunnel_speed;実測値 20 m/s、壁厚 5 cm
スキン >= max(0.01, 半径 x 0.1);ステップ 0.1~0.4 m;最小移動量 02 m の CharacterControllerマニュアル
たわみ = m x g / k;反転時の加速度は 2 倍浮遊カプセル実測;Toyful [00:05:32]
半径あたり 3 ボクセル;内部は 4~6;8 を超えてもほとんど効果なし;ドアはエージェント直径の約 1.5 倍以上エージェントの半径マニュアル;実測
ドリフトがエージェントの半径を超えた場合、ドリフトを delta の 0.9 倍引き戻す(実測では r 0.4 で最大 0.41~0.45 m、補正なしでは 1.84 m)アニメーション駆動エージェントAI Navigation マニュアル
Carve は次のフレームに反映;Carve Only Stationary は 0.5 s 静止後に再カービング(実測で 0.50~0.61 s)NavMesh Obstacleマニュアル;実測
AI の処理時間は 0.10~0.15 ms(Update)対 約 0.01 ms(10 Hz ディレクター)200 エージェント、Editor実測
200 エージェントでは Burst 並列処理はマネージド処理より遅く、2,000 からは高速知覚の事前フィルタリング実測
総当たりはアイテムとクエリの組み合わせあたりマネージドで 7~8 ns、Burst で 1.2~1.5 ns;グリッドは 10,000 x 200 で 0.3~1.3 ms 対 14~15 ms近接クエリ実測(Survival Kids の KD tree:1,000 アイテムに対する 20 クエリで 5 ms から 0.08 ms)
200 ms で認識される;FPS は 100 ms 未満;30 Hz ティック;更新レート > 発射レートネットコードNGO マニュアル
実ボイス 32 / 仮想ボイス 512;優先度 0~256、デフォルト 128オーディオ設定実測;マニュアル
Max Distance を超えるとゲイン = min/d(対数);Max Distance で 0(線形)3D ロールオフ実測
低域通過のカットオフ曲線は 22000~10 Hz、Audio Low Pass Filter が必要距離によるこもりマニュアル;実測では 8 m で 8 kHz:0.03x
スライダー 0.0001..1 -> 20 log10(v);頻繁に鳴るサウンドは 10~15 インスタンスミキサー、ボイスDU7cgVsU2rM [00:14:30];BgpqoRFCNOs [00:17:56]

品質ゲート

  • 測定可能: compile_errors == [];新しいプロセスで設定を読み戻せること;AuditGameplayScene でエラーが 0 件(ネガティブコントロールですべての仕込まれたトラップを検出すること);GAMEPLAY_METRICS を解析したうえでテストスイートがすべて成功すること;実際の速度での CCD およびスイープテスト;プールがベースラインに戻ること;実際の件数での群衆 CSV に p95、最悪フレーム、AI スライスが含まれること; code_audit のエラーが 0 件であること。
  • 視覚: NavMesh オーバーレイと群衆キャプチャが ut_review.image_checks に合格し、実際に開いて確認されていること; 概要で操作感が求められている場合は、フレームからコントローラーと移動の操作感を評価すること。

よくある間違い

間違い発生する問題修正
高速な剛体すべてで常時 Dynamic を使うCPU 負荷が増え、動的な板をすり抜けるまずは Speculative を使い、必要なら SweepProjectile を使用します
太い弾丸に細いレイを使う半径が触れているものを検出できないコライダーの形状をスイープします(Queries.SweepShape)
バッファーを使って NonAlloc ループを行う古いヒットが残る、ヒットが通知なく欠落する、順序が不定になる件数分だけループし、件数 == バッファー長の場合はフラグを立て、ソートします
Physics.autoSyncTransforms = trueクエリの前に毎回、隠れた同期が発生し、CS0618 が出るクエリの前に Physics.SyncTransforms() を1回呼び出します
エディタージョブで Time.maximumDeltaTime = x を設定する次回起動時に 0.333 に戻るTimeManager.asset に対して SerializedObject を使用します
BuildNavMesh() の後にシーンを保存するデータが埋め込まれ、シーンファイルがバイナリになる先に AssetDatabase.CreateAsset(surface.navMeshData) を呼び出します
既存のシーンに上書き保存した後でクエリを実行するNavMesh が空になり、パスが無効になるGameplayScenes.EnsureLoaded を使用します
HeightMesh のない階段エージェントがランプを滑るように上るHeight Mesh をビルドして再ベイクします
障害物が切り抜かれるフレームで経路を再計算する新しい障害物を通る経路になる次のフレームでクエリを実行します
エージェントとアニメーションの両方がキャラクターを動かす足滑りやジッターが発生するAgentLocomotionSync を使い、どちらか一方で動かします
ドメインリロードを無効にした状態で静的シングルトンを使う前回セッションのハブが残り、更新が二重に実行されるSubsystemRegistration でリセットし、SafeUnregister を使用します
FindObjectsByType<Transform> から TransformAccessArray を作成するシーンに応じてフレームコストが増加する独自のリストを管理し、変更時に再構築します
ジョブの実行中に TAA のトランスフォームを書き込むメインスレッドがジョブの完了を待って停止する読み取り専用としてコピーし、Complete() の後に書き込みます
大規模な環境で「あらゆる近くのもの」をトリガーで検出する移動するオブジェクトごとにトランスフォーム同期が発生する独自のリストと Burst を使い、その後グリッドを使います(proximity_gate)
別の名前のファイルに MonoBehaviour を記述するシーンの保存後に通知なく存在しなくなる1ファイルにつき MonoBehaviour を1つにします
戦闘音に PlayClipAtPoint を使うスライダーが機能しないグループにルーティングするプール済みエミッターを使用します
SFX グループに「距離」効果としてローパスをかけるすべてのサウンドが同じようにこもるソースごとに AudioLowPassFilter のカーブを設定します
Logarithmic ソースを無音にするために Max Distance を信頼する遠くでも音が聞こえる(1/d)Linear またはカスタムカーブを使うか、距離でカリングします
計画に「NGO にはクライアント予測がある」と記載する約束したループが存在しないanticipation と手作業で構築した reconciliation を組み合わせるか、Netcode for Entities を使用します

引き継ぎ

  • 受け取るもの: scenario-unity-architecture からコード構造、サービス、Input System アクション、scenario-unity-world-building から Environment レイヤー上のレベル、scenario-unity-animation からアニメーション付きキャラクター、scenario-unity-performance からフレーム予算を受け取ります。
  • 引き渡すもの: locomotion の速度、状態パラメーター、選択した結合方式を scenario-unity-animation に、ヒットイベントを scenario-unity-vfx に、ヘルスとスライダー用のフックを scenario-unity-ui(MixerControl.SetUserVolume)に、群衆 CSV、AI スライス、ジョブの待機を scenario-unity-performance に、2D 物理を scenario-unity-2d に、オーディオの制限を scenario-unity-mobile と scenario-unity-web に引き渡します。パケット:シーン、ジョブ ID、監査 JSON、テスト指標、CSV 判定、Verified、Assumed。

Unity 6.3 に関する注意事項

  • 3D 物理:linearVelocity、linearDamping、PhysicsMaterial、Physics.simulationMode。Physics.autoSyncTransforms は非推奨です(単にオフにするのではありません)。RaycastCommand は QueryParameters を受け取ります。Physics.BakeMesh(int...) は旧式です。
  • AI Navigation 2.0.14:NavMesh Surface ごとにベイクします(Navigation ウィンドウには Bake はありません)。OffMesh Link は非推奨です。
  • Entities 1.4.8(コアは 6.4 から);6.5 では Entities.ForEach が削除されます。6.6 では Burst が組み込みになり、新規プロジェクトでは Play 時のドメインリロードがスキップされます。
  • NGO 2.13.0 がバンドルされ、1.x は非推奨です。[Rpc(SendTo.Server)] を使用します。NetworkTransform.Update ではなく OnUpdate をオーバーライドします。Multiplayer Services は Lobby と Relay に取って代わります。

参考資料

コメント