implementing-gameplay-invariants について
implementing-gameplay-invariantsは、ゲームデザインの意図を実装上の制約と検証項目に変換するスキルです。メカニクスを実装・レビューする開発者向けで、低スキルな入力方針に見えるコストやスコア上限を設け、方針別のテストで確認します。
主な機能
- 01設計上の約束をテスト可能な不変条件に変換
- 02スコア対象のライフサイクルを定義
- 03重複加点や安全な状態での加点を確認
- 04入力方針別の検証プローブを設計
- 05ルールを継続的に示す表示を要求
活用シーン
- 01連打を不利にする設計を実装可能なルールに変換
- 02ゲーム仕様に沿ってメカニクスをレビュー
- 03再利用可能なパルスや反射によるスコア稼ぎを調査
- 04待機や単一入力のスコア上限をテスト
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ abagames/ agentic-gamedev-skills --skill implementing-gameplay-invariantsインストール後、Claude Code で/implementing-gameplay-invariantsを入力して読み込まれたか確認します。
同梱ファイルreferences/invariant-patterns.md
詳細
ゲームプレイの不変条件を実装する
目的
デザイン上の意図を、具体的な実装制約に落とし込みます。このスキルは、ゲームデザインとエンジン固有のコーディングの橋渡しとなり、「連打はタイミングを合わせたプレイより不利である」といった記述を、どのエンジンにも適用できる不変条件、実装パターン、検証チェックに変換します。
ゲーム仕様にリスクと報酬に関する説明があるものの、それをコードでどう強制するかが示されていない場合は、designing-mini-games を使わない場合でもこのスキルを使用してください。
まだゲームのデザイン仕様がない場合は、designing-mini-games から始めてください(1ボタンおよび複数入力の概要を扱います)。
使用する場面
- デザイン仕様や README に基づいてゲームメカニクスを実装する場合。
- バランステストを実行する前に、デザイン仕様に照らしてコードをレビューする場合。
- チェッカーやシミュレーションで
mono_dominant、tied、または反復入力の優位が示され、どのデザイン上の約束にコードレベルの不変条件が必要かがすでに分かっている場合。 - デザインに、何もしないことの弱さ、ホールドのみの弱さ、連打の弱さ、安全な待機、クールダウン、コンボ、スコア加算イベント、シールド、チャージ、または無敵状態が含まれている場合。
- 実装がエンジン固有でも、問題の原因が API レベルではなくルールレベルにある場合。
使用しない場面
- レンダリング、アセット、UI、オーディオ、ビルド、またはエンジン API のみを扱う場合。
- ルールレベルの不変条件がすでに適用されている状態で、数値を調整する場合。
- 中核となるルールやスコアイベントが明らかになる前の、自由度の高いゲームデザインを行う場合。
- テレメトリからバランス修正の方法を選ぶ場合。問題が発生して原因の診断やバランス調整パターンの選択が必要なら、まず
evaluating-gameplay-balanceを使用してください。選択した修正に新しいコードレベルの不変条件が必要な場合は、その後にこのスキルを使用してください。
必要な入力
- デザイン仕様または README。
- 熟練したプレイより不利にすべき入力方針:何もしない、ホールドのみ、連打、常に左/右、安全な待機、またはこれらに相当するもの。
- 意図する熟練プレイのパターン。
- スコアイベント、ゲームオーバー条件、安全状態やパワー状態。
- プロジェクトのチェッカーで使われる決定論的検証シード、または既知の初期乱数列。
- 任意:テレメトリまたはチェッカーの出力。
手順
-
コード上で必ず成立すべき設計上の約束を抽出します。
- 「待機は弱い」
- 「押しっぱなしだけでは弱い」
- 「連打は弱い」
- 「スコアはタイミングによって決まる」
- 「シールドは安全だが、代償がある」
- 「着地/パルス/反射では、正確さに応じて報酬が得られる」
-
各約束を不変条件として書き換えます。
- テスト可能な表現を使います。「連打はよくない」ではなく、「前のタップから12フレーム以内のタップではスコアを獲得できない」とします。
- 適用範囲を含めます。入力ごと、ターゲットごと、ハザードごと、着地ごと、コンボごと、秒ごと、リソース単位ごとなどです。
- 結果を含めます。スコアなし、クールダウン、無防備状態、リソース消費、コンボリセット、狙いの失敗、位置の不利が可視化されることなどです。
- スコア対象がフレームや入力をまたいで存続する場合は、そのライフサイクルを明示的に定義します。生成/開始、スコア獲得可能な有効時間、スコア獲得済み/消費済み状態、有効期限/削除、そして新たなライフサイクルで再びスコアを獲得できるかどうかです。
- パルス、反射、範囲攻撃、シールド、リリースによって繰り返しスコアを獲得できる場合は、可能な限り、重複スコアを検出できるよう検証ログに機会ごとの識別子(
targetId、pulseId、または同等のもの)を記録するよう求めます。
-
仕様から仕組みを推論できる場合は、コーディング前にポリシー固有の事前チェックを実行します。
- 問題のある各ポリシーについて、プレイが破綻するか、安全にタイムアウトするか、スコアに上限を設けた状態で続行できるかを記述します。
- 問題のあるポリシーが無期限に続けられる場合、その不変条件でスコアに上限を設けるか、スコアを獲得できないようにする必要があります。弱点を死亡にしてはいけません。
- ランダムな最初のハザード、隙間、スポーン、ルートが生存可能性に影響する場合は、プロジェクトの決定論的検証シードを事前確認し、最初の5~10回の結果が不変条件の想定と一致することを確認します。
-
必要に応じて、
references/invariant-patterns.mdから実装パターンを選びます。 -
スコア獲得の抜け道を確認します。
- 入力ごとの生スコアを付与しません。
- 新しいハザード/機会が発生しなくても繰り返しスコアを獲得できるパルスを再利用できないようにします。
- 生存が主な熟練度の指標でない限り、安全/低スキル状態のプレイヤーに生存スコアを付与しません。
- スコアに上限のない恒久的な安全状態を作りません。
- スコア獲得済みのオブジェクトが、ライフサイクルのリセットを視覚的に示さずに、将来のスコア獲得対象として残り続けないようにします。
- スパム対策の検証を総スコアだけで行いません。同じオブジェクトから再度スコアを獲得できる仕組みの場合は、機会またはターゲットごとのスコアも比較します。
-
実装を追加またはレビューします。
- エンジン固有のコードは、エンジン用スキルまたはプロジェクトのコードに記述します。
- このスキルでは、満たすべき条件を定義します。描画方法やAPI呼び出しの具体的な方法は定義しません。
-
検証します。
- 単調なポリシーと、探索的/熟練者向けのポリシーを実行します。
- 全体の比率チェックを行う前に、不変条件に特化したプローブを優先します。
NoInputのスコア/死亡、HoldOnlyのスコア/死亡、SpamPressのスコアイベント、エンティティ数、リソース消費、機会ごとのスコア上限を確認します。 - パルス/反射/範囲攻撃の修正では、受理/拒否された入力、パルスの経過時間またはクールダウン状態、ターゲット/機会ID、スコア獲得済み状態、スコア差分、アクティブなエンティティ数の最大値を記録します。
- 可能であれば編集前に、プローブの合否基準を定義します。たとえば、
SpamPressのターゲットごとのスコアがスクリプト化された熟練者向けサイクルを下回ること、HoldOnlyのスコアを指定レート以下に抑えること、アクティブなエンティティ数の最大値を固定上限以下にすることなどです。 - 既知の問題ポリシーでのスコアが、熟練者のプレイより低いことを確認します。
- シミュレーターがない場合は、人が読める短い検証チェックリストを作成し、未対応の項目を明記します。
不変条件テンプレート
仕様書、PRのメモ、コードレビューでは、次の簡潔な形式を使います。
- Design promise: <prose from spec>
- Implementation invariant: <testable rule>
- Pattern: <pattern ID from references/invariant-patterns.md, e.g. cooldown, per-target-cap, resource-drain>
- Readout: <the persistent on-screen indicator that lets the player act on this rule — or why this rule changes no player decision>
- Precheck: <whether the bad policy dies, survives safely, or must be score-capped>
- Validation: <policy, telemetry, or seeded scenario that should fail if the invariant is broken>
Readoutはルールの説明ではなく、ルールそのものの一部です。プレイヤーの行動を変える不変条件が描画されていなければ、不公平なゲームと区別できません。下記の「見えないルール」を参照してください。
よくある失敗パターン
- 文章だけの弱点:READMEでは連打はよくないと説明されていますが、コードではタップするたびにスコア獲得のチャンスがあります。
- 再利用可能なスコア獲得パルス:着地、シールド、範囲攻撃、反射によるパルスで、入力を繰り返すことで何度もスコアを獲得できます。
- 安全なスコア獲得:最も安全な状態でもポイントを獲得できたり、倍率を維持できたりします。
- 隠れたテスター向けパッチ:目に見えるゲームルールを作らず、悪いポリシーをコードで特別扱いしています。
- 数値だけの修正:欠けている不変条件を強制せずに、スポーン率やスコア値を変更しています。
- エンティティの存続期間が無制限:スコア獲得オブジェクトやハザードオブジェクトが長く残りすぎるため、検証が遅くなり、スコア獲得ループが連発します。
- シード固有の想定外:検証用シードによって序盤にハザードや隙間が生成され、設計上の前提に反しますが、問題が見つかるのはGAの実行が完了した後だけです。
- 不変条件の矛盾:設計で相互に両立しない不変条件(例:「何もせずにいると死ぬ」と「生存だけがスコア獲得手段」)を要求しています。この矛盾の解消にコア体験の変更やタグの関連性の破棄が必要な場合は、設計は実現不可能であると報告し、失敗レポートを推奨してください。
- 見えないルール:不変条件は正しく実装され、すべての値のアサーションに合格しているのに、画面には一切表示されません。これは最も発見が難しく、実測上、最もよくある不具合の一つです。ある測定対象のプロジェクトでは、リリース後に修正された9件の不具合のうち4件が、同じ種類の欠陥でした(操作、オーバーロードのトリガー、クリア条件はいずれもシミュレーション上では完全に正しく、プレイヤーには見えませんでした)。値のアサーションでは、仕組み上この不具合を検出できません。シミュレーションは正しいためです。検出に必要なのは、表示要件とスクリーンショットだけです。
検証
次の条件を満たす変更は有効です。
- 各設計上の約束に、少なくとも1つの実装上の不変条件がある。
- 単調な各ポリシーに、目に見えるコストまたはスコア上限がある。
- 無期限に生存できる単調なポリシーには、スコア上限またはスコア獲得を停止するルールがある。
- **プレイヤーの判断を変えるすべての不変条件に、常時表示される表示要素があり、その表示は状況が悪化してから警告するのではなく、悪化する前に満たされていきます。**上限に向けて混雑状況を追跡するゲージはプレイヤーの行動を促します。一方、オーバーロードの発生時に鳴る警告は、敗北を告げるだけです。ルールと同じ変更で表示要素を追加し、スクリーンショットを撮ってください。
- ランダムな初期ハザードや隙間が生存可能性を左右する場合は、シードを固定した序盤のランダムシーケンスを確認している。
- 熟練したプレイによって、スコアの向上または意味のある生存時間の延長につながる因果関係のある方法がある。
- ポリシーの比較、テレメトリー、または具体的な手動チェックリストによって実装をテストできる。
検証が不可能な場合は、不変条件は仕様として定義されているものの、検証されていないと報告してください。
参考資料
references/invariant-patterns.md— 連打、ホールドのみ、何もしない状態、安全な待機、スコア獲得パルス、コンボ、リソース状態の再利用可能なパターン