Physics Debug Validator — Hitbox Alignment
The single most invisible class of AI-generated game bugs: physics bodies that are offset, undersized, or oversized relative to their visual sprites. The player gets hit by an attack that visually missed, or falls through a platform that looks solid. This validator catches all of it.
When to use
In parallel with dynamic_check.mjs and run_qa.mjs during the parallel-qa-orchestra pass. Also run standalone when the user reports "hitboxes feel wrong" or "invisible collision."
Method
- Boot the game in Playwright with a JS init script that enables
physics.arcade.debug = trueon the live Phaser game object (no source-file modification needed). - Wait for
window.__gameReady. - Capture a canvas screenshot — physics debug mode draws colored rectangles around every body.
- Also capture body metadata via
page.evaluate()readingwindow.__game.physics.world.bodies.entries. - Emit a JSON report with body positions, sizes, and their paired sprite positions.
- The host agent (or Claude vision) reviews the screenshot to flag misaligned bodies.
Output contract
{
"ok": true,
"screenshotPath": "qa/physics-debug.png",
"bodies": [
{
"label": "KNIGHT",
"bodyX": 160, "bodyY": 288, "bodyW": 22, "bodyH": 27,
"spriteX": 160, "spriteY": 288, "spriteW": 32, "spriteH": 32,
"offsetX": 0, "offsetY": 0,
"aligned": true
},
{
"label": "SLIME",
"bodyX": 480, "bodyY": 144, "bodyW": 32, "bodyH": 32,
"spriteX": 480, "spriteY": 132, "spriteW": 32, "spriteH": 32,
"offsetY": 12,
"aligned": false,
"issue": "body Y offset 12px below sprite top — may clip floor tile"
}
],
"misalignedCount": 1
}
ok = true means the script ran successfully (not necessarily zero issues — the host agent reviews body data to determine severity).
Physics debug injection (no source modification)
The script uses page.addInitScript() to patch the Phaser Game constructor before it runs:
await page.addInitScript(() => {
const _Game = window.Phaser?.Game;
// Patch applied after Phaser loads via window.__patchPhysicsDebug flag
window.__patchPhysicsDebug = true;
});
// After gameReady, toggle debug on live game:
await page.evaluate(() => {
if (window.__game?.physics?.world) {
window.__game.physics.world.drawDebug = true;
window.__game.physics.world.debugGraphic?.clear?.();
}
});
Known limitations
- Debug rectangles are rendered on a graphics layer; they may not perfectly align with the Phaser camera if the camera is scrolled. The script waits for the player to settle at spawn before screenshotting.
- Kinematic bodies (sensors, fixed) show in a different color (typically cyan vs green for dynamic).
- The host agent should look for bodies where the colored rectangle is significantly (> 20%) outside the visible sprite boundary.
Process
- Run
scripts/validate_physics.mjs <project-dir> [--port N] - Script starts vite dev server on the given port (default: auto-find free port).
- Playwright boots game, injects debug, screenshots.
- Script writes
qa/physics-debug.png+qa/physics-bodies.json. - Host agent reviews both artifacts. Misaligned bodies are fed to refiner as
hitbox-misalignedfailures.
Scripts
scripts/validate_physics.mjs <project-dir> [--port N]— boots, injects debug, screenshots, emits body report.