scaffold-gateables について
スキン、コンティニュー、デイリーチャレンジなど、権利チェックで制御する追加機能を既存ゲームに組み込むスキルです。機能は初期状態でロックされ、コアの遊びを変えず、単一の `isEntitled(key)` を後続の権利システムとの接点にします。
主な機能
- 01共通の権利チェックを作成
- 02シルバー・ゴールド向けの機能フックを構築
- 03EventBus 経由でイベントを追加
- 04ゲーム状態と定数を拡張
- 05ロック中も通常のゲーム進行を維持
活用シーン
- 01任意のスキン選択画面を追加
- 02死亡後のコンティニュー処理を構築
- 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 を返します。この状態でもゲームは完全に通常どおりに感じられる必要があります。後続の収益化レイヤーで権利が有効になると、ゲート可能な機能が使えるようになります。
基本原則
何を提案し、何を却下するかを判断する際は、以下を使用してください。これはガイドラインではなく、ルールです。
- コアループをゲートしてはいけません。 ゲームの中心がジャンプなら、全員がジャンプできるようにします。重力ではなく、スキンをゲートします。
- 減らすより、追加することを優先します。 無料モードを縮小するのではなく、ボーナスモードを追加します。無料プレイヤーが不利益を感じることがないようにします。
- ゲームのリズムに合わせます。 短時間のセッションなら、セッション単位の特典(続行、デイリースキン、デイリーチャレンジのシードなど)。長時間のプレイなら、永続性のある要素(セーブスロット、アンロックツリー、ボーナスチャプターなど)を選びます。
- シルバーとゴールドでは、ゲート可能な機能を 2~3 個提案します。 ブロンズはデフォルトです。すべてのプレイヤーはデフォルトでブロンズの体験を得るため、ブロンズ向けの機能は足場を作りません。シルバーの利便機能を 1~2 個、ゴールドの目玉機能を 1 個目指します。数は目安です。ゲームに本当に合うものを提案してください。
- すべてのゲート可能な機能は、問題なく劣化する必要があります。
isEntitledがfalseを返す場合でも、ゲームは通常どおりに感じられる必要があります。壊れている、あるいは何かが欠けていると感じさせてはいけません。
ティアの用語
ゲームで一般的な進行度ラベルである bronze/silver/gold を使用します。これにより、このスキルは収益化方式に依存せず、プレイヤーにも自然に伝わります。
ブロンズはデフォルトのティアです。すべてのプレイヤーはデフォルトでブロンズです。 ブロンズの体験こそ、誰もが常に楽しめるコアゲームループです。ブロンズ向けの機能は足場を作りません。scaffold-gateables が生成するのは、シルバーとゴールドの機能のみです。
後続の統合レイヤーでは、シルバー/ゴールドを独自の有料ティアシステムに対応付けます。
| scaffold-gateables | sub.games | Play.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 への追加 |
|---|---|---|---|---|---|---|
| スキン選択 | silver | premium_skins | GameOverScene のピッカーで 5 種類のコスメティックバリエーションを選択 | デフォルトスキンのみ。ボタンはグレーアウト | skin:selected, skin:unlocked | selectedSkin: 'default', unlockedSkins: ['default'](リセット後も保持) |
| 死亡後のコンティニュー | silver | continue | セッションごとに 1 回、死亡時にコンティニューを提示 | コンティニューの確認なしで即座に再スタート | continue:offered, continue:accepted | continueOffered: false, usedContinue: false(リセット時にクリア) |
| デイリーチャレンジモード | gold | daily_challenge | 毎日固有のシード値でプレイできる独自のリーダーボード付きラン | デイリーチャレンジのメニューを表示しない | daily:opened, daily:completed | dailyChallengeSeed: 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.jssrc/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(減算ではなく加算)に従ってください。ゲート対象機能がないことで、無料プレイヤーがペナルティを受けたと感じることがあってはなりません。ゲート対象機能を取り除くと無料プレイヤーのゲーム体験が悪くなるなら、制限する対象を間違えています。
出力
ユーザーには次の内容を伝えてください。
- 追加したもの — 各ゲート対象機能(名前、ティア、権利キー)を列挙します
- 単一の接続ポイント —
src/systems/Entitlements.jsを示し、次のように説明します。「キーに対してisEntitled(key)がtrueを返すように切り替えると、その機能が有効になります。次に/monetize-game(Play.fun)または/subgames(サブスクリプション)に接続してください。」 - テスト方法 — 「
npm run devを実行してプレイしてください。すべてのゲート対象機能がデフォルトでロックされていても、ゲームは通常どおりに感じられるはずです。Entitlements.jsを一時的にreturn true;に変更すると、機能が有効になることを確認できます。」 - 次のステップ — 適切に
/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だけです。