mcpField
S
scaffold-gateables

scaffold-gateables

github.com/playableintelligence開発350586 回インストール22 週間前に更新
リポジトリ

scaffold-gateables について

スキン、コンティニュー、デイリーチャレンジなど、権利チェックで制御する追加機能を既存ゲームに組み込むスキルです。機能は初期状態でロックされ、コアの遊びを変えず、単一の `isEntitled(key)` を後続の権利システムとの接点にします。

主な機能

  1. 01共通の権利チェックを作成
  2. 02シルバー・ゴールド向けの機能フックを構築
  3. 03EventBus 経由でイベントを追加
  4. 04ゲーム状態と定数を拡張
  5. 05ロック中も通常のゲーム進行を維持

活用シーン

  1. 01任意のスキン選択画面を追加
  2. 02死亡後のコンティニュー処理を構築
  3. 03デイリーチャレンジやボーナスモードを追加

インストール

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

$npx skills add https://github.com/playableintelligence/game-creator --skill scaffold-gateables

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

詳細

パフォーマンスに関する注意事項

  • 機能を提案する前に、時間をかけてゲームを理解してください
  • 速度より品質が重要です。誤ったゲート対象はゲームに恒久的な悪影響を与えます
  • ステップ 4 のロックされた経路の検証を省略しないでください

ゲート可能な機能の足場を追加する

既存のゲームにゲート可能な機能を追加します。このスキルでは、EventBus 経由で isEntitled(key) という単一の権限チェック箇所につながる「フック」を生成します。たとえば、スキン選択、死亡後の続行フロー、ボーナスモード、セーブスロット、デイリーチャレンジなどです。Play.fun、sub.games、Stripe など、特定の収益化 SDK は追加しません。生成するのは足場のみで、後からどのペイウォール、サブスクリプション、権利レイヤーでも有効化できるようにします。

デフォルトでは、すべてロックされています。isEntitled(key) はすべてのキーに対して false を返します。この状態でもゲームは完全に通常どおりに感じられる必要があります。後続の収益化レイヤーで権利が有効になると、ゲート可能な機能が使えるようになります。

基本原則

何を提案し、何を却下するかを判断する際は、以下を使用してください。これはガイドラインではなく、ルールです。

  1. コアループをゲートしてはいけません。 ゲームの中心がジャンプなら、全員がジャンプできるようにします。重力ではなく、スキンをゲートします。
  2. 減らすより、追加することを優先します。 無料モードを縮小するのではなく、ボーナスモードを追加します。無料プレイヤーが不利益を感じることがないようにします。
  3. ゲームのリズムに合わせます。 短時間のセッションなら、セッション単位の特典(続行、デイリースキン、デイリーチャレンジのシードなど)。長時間のプレイなら、永続性のある要素(セーブスロット、アンロックツリー、ボーナスチャプターなど)を選びます。
  4. シルバーとゴールドでは、ゲート可能な機能を 2~3 個提案します。 ブロンズはデフォルトです。すべてのプレイヤーはデフォルトでブロンズの体験を得るため、ブロンズ向けの機能は足場を作りません。シルバーの利便機能を 1~2 個、ゴールドの目玉機能を 1 個目指します。数は目安です。ゲームに本当に合うものを提案してください。
  5. すべてのゲート可能な機能は、問題なく劣化する必要があります。 isEntitled が false を返す場合でも、ゲームは通常どおりに感じられる必要があります。壊れている、あるいは何かが欠けていると感じさせてはいけません。

ティアの用語

ゲームで一般的な進行度ラベルである bronze/silver/gold を使用します。これにより、このスキルは収益化方式に依存せず、プレイヤーにも自然に伝わります。

ブロンズはデフォルトのティアです。すべてのプレイヤーはデフォルトでブロンズです。 ブロンズの体験こそ、誰もが常に楽しめるコアゲームループです。ブロンズ向けの機能は足場を作りません。scaffold-gateables が生成するのは、シルバーとゴールドの機能のみです。

後続の統合レイヤーでは、シルバー/ゴールドを独自の有料ティアシステムに対応付けます。

scaffold-gateablessub.gamesPlay.fun(ポイント制)
silverサポーター($3~8/月)中程度のしきい値ポイント
goldファウンダー($150/年)高いしきい値ポイント

ステップ 2 の出力でこの対応関係を明示し、/subgames または /monetize-game への引き継ぎ内容を明確にしてください。

手順

ユーザーは $ARGUMENTS で指定したゲーム(パスが指定されていない場合は現在のディレクトリ)に、ゲート可能な機能を追加したいと考えています。パスの後にあるオプションのヒントトークン(例:skins continue)は、提案の優先度に反映します。

ステップ 1:ゲームを理解する
  • package.json を読み、ゲームエンジンを特定します(phaser → Phaser、three → Three.js)。
  • src/core/Constants.js、src/core/EventBus.js、src/core/GameState.js を読みます。
  • src/main.js を読み、window.render_game_to_text() とオーケストレーターのエントリーポイントを確認します。
  • プロジェクトのルートに progress.md があれば読みます(/viral-game パイプラインによって作成されたものです)。
  • 主要なシーン/システムのファイルを読み、コアループを理解します。

その後、作成者に次の一文を伝えます。

コアループは <verb> です。ここはゲートしません。ゲート可能な機能は、コアに追加する形で重ねていきます。

ステップ 2:機能を提案する

5 つの原則に基づき、silver および gold ティアのゲート対象候補を 2~3 個作成します。Markdown テーブルで提示してください。

機能ティアエンタイトルメントキー追加内容ロック時の動作EventBus イベントGameState への追加
スキン選択silverpremium_skinsGameOverScene のピッカーで 5 種類のコスメティックバリエーションを選択デフォルトスキンのみ。ボタンはグレーアウトskin:selected, skin:unlockedselectedSkin: 'default', unlockedSkins: ['default'](リセット後も保持)
死亡後のコンティニューsilvercontinueセッションごとに 1 回、死亡時にコンティニューを提示コンティニューの確認なしで即座に再スタートcontinue:offered, continue:acceptedcontinueOffered: false, usedContinue: false(リセット時にクリア)
デイリーチャレンジモードgolddaily_challenge毎日固有のシード値でプレイできる独自のリーダーボード付きランデイリーチャレンジのメニューを表示しないdaily:opened, daily:completeddailyChallengeSeed: null, dailyBestScore: 0(リセット後も保持)

提案は原則に基づいて選定してください。コアとなるゲームループをロックする提案は却下します。bronze ティアの機能は提案しないでください。bronze は全員に提供されるデフォルトです。短時間プレイのゲームでは、コスメティック > 利便性 > ボーナス > 永続性の順を優先してください。長編ゲームではこの順序を逆にします。

インタラクティブモード(ユーザーがスキルを呼び出した場合): 実装前にクリエイターの確認を待ちます。提案の削除、入れ替え、調整を可能にしてください。

パイプラインモード(/viral-game のステップ 1.25 から呼び出された場合): 上位 2~3 個を自動選定し、確認を求めずに続行します。

ステップ 3:実装する

以下の順序を厳守してください。

3a. src/systems/Entitlements.js を作成する — 機能チェックを一元的に行う接点です。

// src/systems/Entitlements.js
// Single capability check for all gateable features.
// Returns false by default — every gateable must degrade cleanly when locked.
// Wire this to your monetization layer (sub.games, Play.fun, custom paywall)
// by replacing the body with real checks. See `/monetize-game` or `/subgames`.

export function isEntitled(key) {
  // TODO: wire to monetization layer.
  return false;
}

3b. src/core/EventBus.js にイベントを追加する(追記のみとし、domain:action 形式にします)。

3c. src/core/GameState.js に状態フィールドを追加する。reset() を更新し、永続フィールド(例:unlockedSkins、bonusModeUnlocked)を保持し、一時フィールド(例:usedContinue、continueOffered)をクリアします。

3d. src/core/Constants.js の新しい GATEABLES セクションに定数を追加する(スキンリスト、コンティニューのクールダウン、ボーナスモードの設定など)。

3e. ゲームのレイアウトに合わせて、既存の src/ サブディレクトリにゲート対象モジュールを作成する:

  • src/ui/SkinPicker.js(Phaser シーンまたは Three.js パネル)
  • src/systems/ContinueFlow.js
  • src/systems/BonusMode.js
  • など

各ゲート対象のエントリーポイントで isEntitled(key) を呼び出し、分岐させます。

import { isEntitled } from '../systems/Entitlements.js';

function openSkinPicker() {
  if (!isEntitled('premium_skins')) {
    // Locked path: greyed-out button with lock icon, no picker opens.
    // Default skin remains selected. Game continues normally.
    return;
  }
  // Entitled path: open the picker scene.
  scene.scene.launch('SkinPickerScene');
}

ゲームプレイ開始前にタイトル画面やスキン選択画面を表示するフローは絶対に作成しないでください。 テンプレートはゲームプレイを直接開始します。ゲート対象の UI は、ポーズメニュー、設定、または GameOverScene からのみ開きます。

3f. src/main.js の window.render_game_to_text() を追加形式で更新する — 新しい監視可能なフィールド(selectedSkin、continueAvailable、bonusModeActive)を追加し、既存のフィールドは削除しません。これは AI テストハーネスに不可欠です。

ステップ 4:検証する
  • npm run build がエラーなく完了すること。
  • npm run dev を実行し、isEntitled が false(デフォルト)を返す状態でプレイすること。コアのゲームループがスキル導入前とまったく同じ感覚である必要があります。 壊れている、または欠けていると感じる点があれば、コアのゲームループをロックしています。変更を元に戻して修正してください。
  • Entitlements.js を一時的に編集し、すべてのキーに対して return true; を返すようにします。各機能が正しく有効になることを確認してから、変更を元に戻します。
  • tests/ があり、スナップショットで render_game_to_text() を完全一致(toEqual)で比較している場合は、ベースラインを更新します。フィールドの追加は意図した変更です。テストで toMatchObject を使っている場合は、更新不要です。
  • プロジェクトのルートにある progress.md に ## Gateables セクションを追記し、以下を記載します。
    • 追加した機能(名前、ティア、エンタイトルメントキー)
    • 追加した EventBus イベント
    • 追加した GameState フィールド(永続または一時の区別を付記)
    • 追加した定数キー
    • ロック時の動作の説明(機能ごとに 1 行)

トラブルシューティング

権利がある場合は機能するが、ロックするとゲームが壊れる

原因: コアループの一部を制限しているか、ロック時の処理が不完全です。 修正: 元に戻してください。ロック時も通常どおりに感じられるゲームプレイを実現する必要があります。isEntitled が false を返す状態で実行してテストしてください。何かが欠けていたり壊れていたりする場合は、修正してください。

render_game_to_text の更新後にスナップショットテストが失敗する

原因: テストで出力に対して toEqual による完全一致を使っているため、新しいフィールドを追加すると失敗します。 修正: ベースラインを再生成してください。追加した内容は意図したものであり、余分なキーを許容するコンシューマーとの後方互換性があります。

スキン選択画面が初回プレイを妨げる

原因: ゲームプレイ前に選択画面が起動します。 修正: エントリーポイントをポーズメニュー、設定画面、または GameOverScene に移してください。テンプレートはゲームプレイに直接移行するため、引き続きその動作を維持する必要があります。

Entitlements.js がすでに存在する

原因: このスキルが以前に実行されたか、収益化レイヤーがすでに組み込まれています。 修正: ファイルを確認してください。スタブ(return false)の場合は、新しいキーを追加して続行してください。実際の収益化レイヤーに接続されている場合は、そのままにして、新しい関数分岐として新しい権利キーのみを追加してください。

提案が過剰に制限している、またはペナルティのように感じられる

原因: 無料プレイの体験を制限する機能を選んでいます。 修正: 原則 2(減算ではなく加算)に従ってください。ゲート対象機能がないことで、無料プレイヤーがペナルティを受けたと感じることがあってはなりません。ゲート対象機能を取り除くと無料プレイヤーのゲーム体験が悪くなるなら、制限する対象を間違えています。

出力

ユーザーには次の内容を伝えてください。

  1. 追加したもの — 各ゲート対象機能(名前、ティア、権利キー)を列挙します
  2. 単一の接続ポイント — src/systems/Entitlements.js を示し、次のように説明します。「キーに対して isEntitled(key) が true を返すように切り替えると、その機能が有効になります。次に /monetize-game(Play.fun)または /subgames(サブスクリプション)に接続してください。」
  3. テスト方法 — 「npm run dev を実行してプレイしてください。すべてのゲート対象機能がデフォルトでロックされていても、ゲームは通常どおりに感じられるはずです。Entitlements.js を一時的に return true; に変更すると、機能が有効になることを確認できます。」
  4. 次のステップ — 適切に /monetize-game または /subgames を提案します。

使用例

現在のゲームディレクトリでのデフォルトの提案
/scaffold-gateables

結果:ゲームを読み込み、2~3 個のゲート対象機能を提案し、確認後に実装します。Entitlements.js、新しい UI モジュール、新しいイベントと状態を作成します。権利の接続ポイントを切り替えるまでは、ゲームの感覚は変わりません。

ヒントを使った方向性のある提案
/scaffold-gateables skins continue

結果:スキン選択画面と死亡後のコンティニュー機能を優先した提案になります。ゲームにより適している場合は、ほかの提案も可能です。

明示的なプロジェクトパス
/scaffold-gateables ./examples/flappy-bird

結果:現在の作業ディレクトリではなく、指定されたディレクトリに対して操作します。

ヒント

この操作はゲームごとに一度実行してください。後からゲート対象機能を追加したい場合は、個別の追加には /add-feature を使うか、このスキルをもう一度実行してください。既存の Entitlements.js を検出し、新しいキーのみを追加します。

次は統合です。/monetize-game は Entitlements.js を介して Play.fun のポイントをゲート対象機能に接続します。/subgames(subdotgames/skills リポジトリからインストール。別途インストールが必要)はサブスクリプションのティアを接続します。いずれの場合も、ゲート対象機能を有効にするために変更が必要なのは Entitlements.js だけです。

コメント