phaser-playtest について
phaser-playtestは、Phaser 4ゲームのランタイム検証スキルです。Playwrightベースのハーネスでブラウザーの読み込み、シーン、描画、アセット、FPS、エラーを確認し、回帰テスト用のシナリオも実行できます。
主な機能
- 01ヘッドレスChromiumでのランタイム検査
- 02シーン、キャンバス、アセットの検証
- 03FPS計測とエラー検出
- 04スクリプト入力と状態アサーション
- 05開発・ビルドのテストモード
- 06JSONレポートとスクリーンショット
活用シーン
- 01TypeScript検査だけでは分からないゲーム変更の検証
- 02ゲームの受け入れ条件のテスト
- 03スクリプト化したプレイセッションの再現
- 04CIでのゲーム検査
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ yakoub-ai/ phaser4-gamedev --skill phaser-playtestインストール後、Claude Code で/phaser-playtestを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
examples/scenario.example.mjsreferences/instrumenting-games.mdplaytest-harness.mdscripts/playtest.mjs詳細
Phaser 4 プレイテスト(ランタイム検証)
npx tsc --noEmit はコードがコンパイルできることを証明します。しかし、ゲームが実行されるかどうかについては何も示しません。以下の失敗はすべて型チェックを完璧に通過し、それでも壊れたゲームを出荷してしまいます:
| 失敗 | プレイヤーに見えるもの | TypeScript が言うこと |
|---|---|---|
| アセットパスのタイポ | 見えないスプライト | 何も言わない — 文字列だから |
scene: [] にシーンが含まれていない | 黒い画面 | 何も言わない |
create() が最初の行の後で例外を投げる | 半分しか構築されていないシーン | 何も言わない |
| depth/alpha/カメラスクロールのミス | 空白のキャンバス | 何も言わない |
| 物理ボディが追加されていない | プレイヤーが床をすり抜ける | 何も言わない |
| 上限のないエミッター | 12 fps | 何も言わない |
ランタイム検証は任意ではありません。 シーンのライフサイクル、アセットの読み込み、物理、またはレンダリングに触れる変更を行った後は、ユーザーに動作すると伝える前にハーネスを実行してください。
実行する
node "${CLAUDE_PLUGIN_ROOT}/skills/phaser-playtest/scripts/playtest.mjs" --project .
ハーネスは開発サーバー自体を起動し、ヘッドレス Chromium を操作し、サーバーを再びシャットダウンします。設定もテストファイルも不要です。
プロジェクトに Playwright が必要です(一度だけ):
npm install -D playwright && npx playwright install chromium
チェック内容
- ページが読み込まれる — ドキュメントの HTTP ステータス
- キャンバスが作成される — サイズを持つ
<canvas>が存在する(失敗 ⇒ Phaser が起動していない) - Phaser インスタンスに到達できる —
window上のPhaser.Gameを見つける - ゲームが起動している —
isBooted === true - アクティブなシーン — どのシーンが実行され、それぞれが保持する表示オブジェクトの数
- シーンがコンテンツをレンダリングする — 表示リストが空のシーンに対して警告
- フレームレート — 実際のフレームでサンプリングした FPS の中央値と第5パーセンタイル
- キャンバスがコンテンツをレンダリングする — ピクセル分析;黒い画面を検出
- 未捕捉の例外がない — スタック付きの
pageerror - コンソールエラーがない
- すべてのアセットが読み込まれる — 開発サーバーが
200 text/htmlとして偽装するファイルも含む
終了コード 0 = 合格、1 = チェック失敗、2 = ハーネスエラー。.playtest/report.json と PNG スクリーンショットを書き込みます。
オプション
| フラグ | 目的 |
|---|---|
--project DIR | プロジェクトルート(デフォルト: cwd) |
--url URL | サーバーを起動する代わりに、すでに実行中のサーバーをテストする |
--mode dev|build | build は npm run build + preview を実行する — ベースパスとバンドルに関するバグを検出する |
--scenario FILE | スクリプト化されたプレイセッションを実行する(以下を参照) |
--device iphone|android | モバイルのビューポート、DPR、タッチ、UA |
--settle MS | 調査前に待機する(デフォルト 3000;遅いプリロードの場合は増やす) |
--fail-on-warn | コンソール警告を失敗として扱う(CI) |
--json | JSON のみを出力する — 結果を解析する場合に使用する |
--headed | ブラウザを表示する |
ゲームをテスト可能にする(1行)
詳細なチェックにはゲームインスタンスが必要です。バンドルされたゲームはそれをモジュールスコープに保持しているため、開発ビルドでのみ公開します:
const game = new Phaser.Game(config);
if (import.meta.env.DEV) (window as any).__PHASER_GAME__ = game;
export default game;
これがなくてもハーネスは黒い画面、例外、404 を検出しますが、シーン、FPS、状態のアサーションはスキップされます — そしてその旨を報告します。この行を追加してください;本番環境では何のコストもかかりません。決定的なテストのためのフック(シード付き RNG、時間制御、状態注入)については references/instrumenting-games.md を参照してください。
シナリオスクリプト
シナリオは入力を操作し、実行中のゲーム状態に対してアサーションを行います。game はすべての expression 内で実行中のインスタンスにバインドされます。
// playtest/combat.mjs
export default [
{ name: 'reaches gameplay', action: 'expect',
expect: { expression: `game.scene.isActive('GameScene')`, equals: true } },
{ name: 'walk right', action: 'key', key: 'ArrowRight', duration: 600 },
{ name: 'player advanced', action: 'expect',
expect: { expression: `game.scene.getScene('GameScene').player.x > 400`, equals: true } },
{ name: 'attack lands', action: 'press', key: 'Space',
expect: { expression: `game.registry.get('enemyHp')`, atMost: 90 } },
{ name: 'after-combat', action: 'screenshot' },
{ name: 'holds frame rate', action: 'expect',
expect: { expression: `game.loop.actualFps`, atLeast: 55 } },
];
アクション: wait(ms)、key(key、duration)、press(key)、click(x、y キャンバス相対)、screenshot(name)、expect。
アサーション: equals(深い比較)、atLeast、atMost、または真値のための裸の expression。
完全なリファレンス: references/playtest-harness.md。
実例: examples/scenario.example.mjs。
プランをテストに変換する
/phaser-gdd または phaser-architect エージェントからの受け入れ基準は、
そのままシナリオのステップに変換できます。シナリオは機能が仕様化された時点で書きましょう。
壊れてからではありません。
| 受け入れ基準 | シナリオのアサーション |
|---|---|
| 「プレイヤーは 3 タイルの高さまでジャンプする」 | game.scene.getScene('GameScene').player.y、atMost: spawnY - 96 |
| 「敵は 3 回で倒れる」 | 攻撃を 3 回押し、その後 enemy.active、equals: false |
| 「スコアはシーンをまたいで保持される」 | GameOver を開始し、その後 game.registry.get('score')、atLeast: 10 |
| 「敵 50 体で 60fps で動作する」 | eval でスポーンし、その後 game.loop.actualFps、atLeast: 55 |
失敗を読み解く
| ハーネスの出力 | 最初に確認すべき根本原因 |
|---|---|
canvas created が失敗する | シーンが constructor/init で例外を投げた、またはバンドルが 500 を返している。コンソールエラーを確認してください。 |
active scenes: no scene is running | シーンが scene: [] から抜けている、または create() が完了前に例外を投げた |
canvas renders content が空白、シーンはアクティブ | オブジェクトがカメラ外、alpha: 0、depth の誤り、またはカメラが追従していない |
scenes render content が空で警告 | create() が早期に return した — 通常は try に飲み込まれた例外 |
all assets load が text/html で失敗する | パスのタイプミス、またはアセットが public/ ではなく src/ にある |
frame rate が低い | パーティクルの上限なし、オブジェクトプーリングなし、フレームごとのアロケーション。/phaser-analyze を使用してください。 |
Phaser game instance found が警告 | 上に __PHASER_GAME__ の行を追加してください |
CI
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx tsc --noEmit
- run: node scripts/playtest.mjs --project . --mode build --fail-on-warn
playtest.mjs をプロジェクト自身の scripts/ にコピーして、CI がプラグインのインストールに依存しないようにしましょう。
規律
- 機能を完了と報告する前にハーネスを実行しましょう — ユーザーがバグを報告してからではありません。
tscが通ってもプレイテストが失敗するなら、その作業は完了していません。- 失敗したときは、コードを編集する前に
.playtest/report.jsonとスクリーンショットを読みましょう。 調査優先は、phaser-debuggerとまったく同じようにここでも当てはまります。 - 主要な機能ごとにシナリオを
playtest/配下に保ちましょう。それらは回帰テストです。
失敗がプレイヤー由来だった場合
コードを変更したからではなく、誰かがバグを報告したためにここに来たのであれば、
すぐにシナリオを書くのではなく skills/phaser-feedback/ を使用してください。これは
まず報告をトリアージすること(「難しすぎる」という不満は欠陥ではなく、再現手順もありません)を
カバーし、各主張の形を適切な構文に対応付けます — 「ときどき」には --repeat、「動かなくなる」には sample、
「時間がかかりすぎる」には waitFor、「時間とともに遅くなる」には --heap です。