phaser-playtest

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Phaser 4 Playtesting (Runtime Verification)

Phaser 4 游戏测试(运行时验证)

npx tsc --noEmit
proves the code compiles. It says nothing about whether the game runs. Every failure below type-checks perfectly and still ships a broken game:
FailureWhat the player seesWhat TypeScript says
Asset path typoInvisible spritesNothing — it's a string
Scene missing from
scene: []
Black screenNothing
create()
throws after first line
Half-built sceneNothing
Depth/alpha/camera-scroll mistakeBlank canvasNothing
Physics body never addedPlayer falls through floorNothing
Uncapped emitter12 fpsNothing
Runtime verification is not optional. Run the harness after any change that touches scene lifecycle, asset loading, physics, or rendering — before telling the user it works.
npx tsc --noEmit
只能证明代码可编译,但无法说明游戏能否运行。以下所有故障都能通过类型检查,却会交付一个无法正常运行的游戏:
故障类型玩家看到的现象TypeScript的反馈
资源路径拼写错误精灵不可见无提示——因为这是字符串类型
场景未添加到
scene: []
配置中
黑屏无提示
create()
方法在执行第一行后抛出异常
场景加载不完整无提示
深度/透明度/相机滚动设置错误空白画布无提示
未添加物理体玩家穿过地面掉落无提示
粒子发射器未限制数量帧率仅12fps无提示
运行时验证必不可少。任何涉及场景生命周期、资源加载、物理系统或渲染的代码修改后,都要先运行此测试工具,再告知用户功能正常。

Run It

运行方式

bash
node "${CLAUDE_PLUGIN_ROOT}/skills/phaser-playtest/scripts/playtest.mjs" --project .
The harness starts the dev server itself, drives headless Chromium, and shuts the server down again. No config, no test files required.
Requires Playwright in the project (once):
bash
npm install -D playwright && npx playwright install chromium
bash
node "${CLAUDE_PLUGIN_ROOT}/skills/phaser-playtest/scripts/playtest.mjs" --project .
该测试工具会自行启动开发服务器,驱动无头Chromium浏览器,测试完成后自动关闭服务器。无需配置,也无需编写测试文件。
项目中需提前安装Playwright(仅需一次):
bash
npm install -D playwright && npx playwright install chromium

What it checks

检查内容

  1. Page loads — HTTP status of the document
  2. Canvas created — a sized
    <canvas>
    exists (fails ⇒ Phaser never booted)
  3. Phaser instance reachable — finds the
    Phaser.Game
    on
    window
  4. Game booted
    isBooted === true
  5. Active scenes — which scenes run, and how many display objects each holds
  6. Scenes render content — warns on a scene with an empty display list
  7. Frame rate — median and 5th-percentile FPS sampled over real frames
  8. Canvas renders content — pixel analysis; catches the black screen
  9. No uncaught exceptions
    pageerror
    with stack
  10. No console errors
  11. All assets load — including files the dev server masks as
    200 text/html
Exit code
0
= pass,
1
= check failures,
2
= harness error. Writes
.playtest/report.json
plus PNG screenshots.
  1. 页面加载情况——文档的HTTP状态码
  2. Canvas是否创建——是否存在已设置尺寸的
    <canvas>
    元素(失败则表示Phaser从未启动)
  3. Phaser实例是否可访问——在
    window
    对象上找到
    Phaser.Game
    实例
  4. 游戏是否启动完成——
    isBooted === true
  5. 活跃场景——运行中的场景有哪些,每个场景包含多少显示对象
  6. 场景是否渲染内容——若场景的显示列表为空则发出警告
  7. 帧率——采样真实帧数据得到的中位数和第5百分位FPS
  8. Canvas是否渲染内容——像素分析,可排查黑屏问题
  9. 是否存在未捕获异常——带有调用栈的
    pageerror
  10. 控制台是否有错误
  11. 所有资源是否加载成功——包括被开发服务器伪装成
    200 text/html
    的文件
退出码
0
表示测试通过,
1
表示检查项失败,
2
表示测试工具自身出错。测试完成后会生成
.playtest/report.json
文件及PNG截图。

Options

可选参数

FlagPurpose
--project DIR
Project root (default: cwd)
--url URL
Test an already-running server instead of starting one
--mode dev|build
build
runs
npm run build
+
preview
— catches base-path and bundling bugs
--scenario FILE
Drive a scripted play session (see below)
--device iphone|android
Mobile viewport, DPR, touch, and UA
--settle MS
Wait before probing (default 3000; raise for slow preloads)
--fail-on-warn
Treat console warnings as failures (CI)
--json
Emit only JSON — use when parsing results
--headed
Show the browser
参数用途
--project DIR
项目根目录(默认:当前工作目录)
--url URL
测试已运行的服务器,而非启动新服务器
--mode dev|build
build
模式会先执行
npm run build
+
preview
——可捕获基础路径和打包相关问题
--scenario FILE
执行脚本化的游戏测试流程(详见下文)
--device iphone|android
模拟移动设备视口、设备像素比、触摸事件及用户代理
--settle MS
等待时间(默认3000毫秒;若预加载较慢可延长)
--fail-on-warn
将控制台警告视为测试失败(适用于CI环境)
--json
仅输出JSON格式结果——适用于解析测试结果的场景
--headed
显示浏览器窗口

Make the Game Testable (one line)

让游戏可测试(仅需一行代码)

The deep checks need the game instance. Bundled games keep it in module scope, so expose it in dev builds only:
typescript
const game = new Phaser.Game(config);
if (import.meta.env.DEV) (window as any).__PHASER_GAME__ = game;
export default game;
Without it the harness still catches black screens, exceptions, and 404s, but skips scene, FPS, and state assertions — and says so. Add the line; it costs nothing in production. See
references/instrumenting-games.md
for deterministic-testing hooks (seeded RNG, time control, state injection).
深度检查需要获取游戏实例。打包后的游戏会将实例保存在模块作用域中,因此只需在开发构建中暴露它:
typescript
const game = new Phaser.Game(config);
if (import.meta.env.DEV) (window as any).__PHASER_GAME__ = game;
export default game;
如果不添加此行,测试工具仍可检测黑屏、异常和404错误,但会跳过场景、帧率和状态断言——并会提示相关信息。添加此行不会对生产环境造成任何影响。如需确定性测试钩子(种子化随机数生成器、时间控制、状态注入),可参考
references/instrumenting-games.md

Scenario Scripts

场景脚本

A scenario drives input and asserts on live game state.
game
is bound to the running instance inside every
expression
.
javascript
// 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 } },
];
Actions:
wait
(
ms
),
key
(
key
,
duration
),
press
(
key
),
click
(
x
,
y
canvas-relative),
screenshot
(
name
),
expect
. Assertions:
equals
(deep),
atLeast
,
atMost
, or bare
expression
for truthy.
Full reference:
references/playtest-harness.md
. Worked example:
examples/scenario.example.mjs
.
场景脚本可驱动输入操作并对实时游戏状态进行断言。在每个
expression
中,
game
会绑定到当前运行的游戏实例。
javascript
// playtest/combat.mjs
export default [
  { name: '进入游戏玩法场景', action: 'expect',
    expect: { expression: `game.scene.isActive('GameScene')`, equals: true } },

  { name: '向右行走', action: 'key', key: 'ArrowRight', duration: 600 },

  { name: '玩家已前进', action: 'expect',
    expect: { expression: `game.scene.getScene('GameScene').player.x > 400`, equals: true } },

  { name: '攻击命中', action: 'press', key: 'Space',
    expect: { expression: `game.registry.get('enemyHp')`, atMost: 90 } },

  { name: '战斗结束后', action: 'screenshot' },

  { name: '帧率达标', action: 'expect',
    expect: { expression: `game.loop.actualFps`, atLeast: 55 } },
];
支持的操作:
wait
(等待毫秒数
ms
)、
key
(按键
key
,持续时间
duration
)、
press
(按键
key
)、
click
(点击坐标
x
y
,基于Canvas相对位置)、
screenshot
(截图名称
name
)、
expect
(断言)。 支持的断言:
equals
(深度相等)、
atLeast
(至少)、
atMost
(至多),或直接使用
expression
判断是否为真值。
完整参考文档:
references/playtest-harness.md
。 示例:
examples/scenario.example.mjs

Turning a Plan into Tests

将需求转化为测试

Acceptance criteria from
/phaser-gdd
or the phaser-architect agent translate directly into scenario steps. Write the scenario when the feature is specified, not after it breaks:
Acceptance criterionScenario assertion
"Player jumps 3 tiles high"
game.scene.getScene('GameScene').player.y
,
atMost: spawnY - 96
"Enemy dies in 3 hits"press attack ×3, then
enemy.active
,
equals: false
"Score persists across scenes"start GameOver, then
game.registry.get('score')
,
atLeast: 10
"Runs at 60fps with 50 enemies"spawn via
eval
, then
game.loop.actualFps
,
atLeast: 55
来自
/phaser-gdd
或phaser-architect agent的验收标准可直接转化为场景步骤。应在功能需求确定时编写场景脚本,而非在功能出现问题后:
验收标准场景断言
"玩家可跳跃3格高"
game.scene.getScene('GameScene').player.y
,
atMost: spawnY - 96
"敌人受3次攻击后死亡"按下攻击键3次,然后断言
enemy.active
,
equals: false
"分数在场景切换时保留"进入游戏结束场景,然后断言
game.registry.get('score')
,
atLeast: 10
"50个敌人时仍保持60fps"通过
eval
生成敌人,然后断言
game.loop.actualFps
,
atLeast: 55

Reading a Failure

故障排查

Harness outputRoot cause to check first
canvas created
fails
Scene threw in
constructor
/
init
, or the bundle 500s. Read console errors.
active scenes: no scene is running
Scene missing from
scene: []
, or
create()
threw before completing
canvas renders content
blank, scenes active
Objects off-camera,
alpha: 0
, wrong depth, or camera not following
scenes render content
warns empty
create()
returned early — usually an exception swallowed by a
try
all assets load
fails with
text/html
Path typo, or the asset is in
src/
instead of
public/
frame rate
low
Uncapped particles, no object pooling, per-frame allocation. Use
/phaser-analyze
.
Phaser game instance found
warns
Add the
__PHASER_GAME__
line above
测试工具输出首先排查的根本原因
canvas created
失败
场景在
constructor
/
init
中抛出异常,或打包文件返回500错误。查看控制台错误信息。
active scenes: no scene is running
场景未添加到
scene: []
配置中,或
create()
方法在执行完成前抛出异常
canvas renders content
显示空白,但场景处于活跃状态
对象在相机视野外、
alpha: 0
、深度设置错误,或相机未跟随对象
scenes render content
警告显示列表为空
create()
方法提前返回——通常是因为异常被
try
语句捕获
all assets load
失败且返回
text/html
路径拼写错误,或资源放在
src/
目录而非
public/
目录
frame rate
偏低
粒子发射器未限制数量、未使用对象池、每帧都有内存分配。可使用
/phaser-analyze
工具分析。
Phaser game instance found
发出警告
添加上文提到的
__PHASER_GAME__
代码行

CI

CI集成

yaml
- 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
Copy
playtest.mjs
into the project's own
scripts/
so CI does not depend on the plugin being installed.
yaml
- 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环境就无需依赖该插件的安装。

Discipline

规范要求

  • Run the harness before reporting a feature complete — not after the user reports a bug.
  • A passing
    tsc
    plus a failing playtest means the work is not done.
  • When it fails, read
    .playtest/report.json
    and the screenshots before editing code. Investigation-first applies here exactly as in
    phaser-debugger
    .
  • Keep a scenario per major feature under
    playtest/
    ; they are regression tests.
  • 在报告功能完成之前运行测试工具——而非在用户报告bug之后。
  • tsc
    检查通过但游戏测试失败,说明工作未完成
  • 测试失败时,先查看
    .playtest/report.json
    和截图,再修改代码。这与
    phaser-debugger
    中的“先调查再修改”原则一致。
  • 为每个主要功能在
    playtest/
    目录下保留一个场景脚本——它们可作为回归测试用例。