add file logging via Vite middleware to ZoomTestScene

Vite dev server gets a /api/log middleware (POST appends to
game-test.log, DELETE clears it). ZoomTestScene writes a zoom event
with before/after state on every scroll, plus a full snapshot every
2 seconds. Log entries are newline-delimited JSON.
This commit is contained in:
2026-03-21 11:19:54 +00:00
parent 007d5b3fee
commit 7c130763b5
2 changed files with 123 additions and 9 deletions

View File

@@ -1,17 +1,19 @@
import Phaser from 'phaser'
import { TILE_SIZE } from '../config'
const GRID_TILES = 50 // world size in tiles
const MIN_ZOOM = 0.25
const MAX_ZOOM = 4.0
const ZOOM_STEP = 0.1
const MARKER_EVERY = 5 // small crosshair every N tiles
const LABEL_EVERY = 10 // coordinate label every N tiles
const CAMERA_SPEED = 400 // px/s
const GRID_TILES = 50 // world size in tiles
const MIN_ZOOM = 0.25
const MAX_ZOOM = 4.0
const ZOOM_STEP = 0.1
const MARKER_EVERY = 5 // small crosshair every N tiles
const LABEL_EVERY = 10 // coordinate label every N tiles
const CAMERA_SPEED = 400 // px/s
const SNAPSHOT_EVERY = 2000 // ms between periodic log snapshots
/**
* First test scene: observes pure Phaser default zoom behavior.
* No custom scroll compensation — cam.setZoom() only, zoom anchors to camera center.
* Logs zoom events and periodic snapshots to /api/log (written to game-test.log).
*
* Controls: Scroll wheel to zoom, WASD / Arrow keys to pan.
*/
@@ -27,12 +29,17 @@ export class ZoomTestScene extends Phaser.Scene {
a: Phaser.Input.Keyboard.Key
d: Phaser.Input.Keyboard.Key
}
private snapshotTimer = 0
constructor() {
super({ key: 'ZoomTest' })
}
create(): void {
// Clear log file at scene start
fetch('/api/log', { method: 'DELETE' })
this.writeLog('scene_start', { tileSize: TILE_SIZE, gridTiles: GRID_TILES })
this.drawGrid()
this.setupCamera()
this.setupInput()
@@ -111,6 +118,7 @@ export class ZoomTestScene extends Phaser.Scene {
/**
* Registers scroll wheel zoom and stores keyboard key references.
* Zoom uses cam.setZoom() only — pure Phaser default, anchors to camera center.
* Each zoom event is logged immediately with before/after state.
*/
private setupInput(): void {
const cam = this.cameras.main
@@ -128,13 +136,42 @@ export class ZoomTestScene extends Phaser.Scene {
}
this.input.on('wheel', (
_ptr: Phaser.Input.Pointer,
ptr: Phaser.Input.Pointer,
_objs: unknown,
_dx: number,
dy: number
) => {
const zoomBefore = cam.zoom
const scrollXBefore = cam.scrollX
const scrollYBefore = cam.scrollY
const newZoom = Phaser.Math.Clamp(cam.zoom - Math.sign(dy) * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
cam.setZoom(newZoom)
// Log after Phaser has applied the zoom (next microtask so values are updated)
setTimeout(() => {
this.writeLog('zoom', {
direction: dy > 0 ? 'out' : 'in',
zoomBefore: +zoomBefore.toFixed(4),
zoomAfter: +cam.zoom.toFixed(4),
scrollX_before: +scrollXBefore.toFixed(2),
scrollY_before: +scrollYBefore.toFixed(2),
scrollX_after: +cam.scrollX.toFixed(2),
scrollY_after: +cam.scrollY.toFixed(2),
scrollX_delta: +(cam.scrollX - scrollXBefore).toFixed(2),
scrollY_delta: +(cam.scrollY - scrollYBefore).toFixed(2),
mouseScreen: { x: +ptr.x.toFixed(1), y: +ptr.y.toFixed(1) },
mouseWorld: { x: +ptr.worldX.toFixed(2), y: +ptr.worldY.toFixed(2) },
centerWorld_after: {
x: +(cam.scrollX + (cam.width / cam.zoom) / 2).toFixed(2),
y: +(cam.scrollY + (cam.height / cam.zoom) / 2).toFixed(2),
},
vpTiles_after: {
w: +((cam.width / cam.zoom) / TILE_SIZE).toFixed(3),
h: +((cam.height / cam.zoom) / TILE_SIZE).toFixed(3),
},
})
}, 0)
})
}
@@ -157,6 +194,13 @@ export class ZoomTestScene extends Phaser.Scene {
update(_time: number, delta: number): void {
this.handleKeyboard(delta)
this.updateOverlay()
// Periodic snapshot
this.snapshotTimer += delta
if (this.snapshotTimer >= SNAPSHOT_EVERY) {
this.snapshotTimer = 0
this.writeSnapshot()
}
}
/**
@@ -236,4 +280,46 @@ export class ZoomTestScene extends Phaser.Scene {
this.logText.setText(lines)
}
/**
* Writes a periodic full-state snapshot to the log.
*/
private writeSnapshot(): void {
const cam = this.cameras.main
const ptr = this.input.activePointer
const vpW = cam.width / cam.zoom
const vpH = cam.height / cam.zoom
this.writeLog('snapshot', {
zoom: +cam.zoom.toFixed(4),
scrollX: +cam.scrollX.toFixed(2),
scrollY: +cam.scrollY.toFixed(2),
vpScreen: { w: cam.width, h: cam.height },
vpWorld: { w: +vpW.toFixed(2), h: +vpH.toFixed(2) },
vpTiles: { w: +((vpW / TILE_SIZE).toFixed(3)), h: +((vpH / TILE_SIZE).toFixed(3)) },
centerWorld: {
x: +(cam.scrollX + vpW / 2).toFixed(2),
y: +(cam.scrollY + vpH / 2).toFixed(2),
},
mouse: {
screen: { x: +ptr.x.toFixed(1), y: +ptr.y.toFixed(1) },
world: { x: +ptr.worldX.toFixed(2), y: +ptr.worldY.toFixed(2) },
},
})
}
/**
* POSTs a structured log entry to the Vite dev server middleware.
* Written to game-test.log in the project root.
* @param event - Event type label
* @param data - Payload object to serialize as JSON
*/
private writeLog(event: string, data: Record<string, unknown>): void {
const entry = JSON.stringify({ t: Date.now(), event, ...data })
fetch('/api/log', {
method: 'POST',
headers: { 'Content-Type': 'text/plain' },
body: entry,
}).catch(() => { /* swallow if dev server not running */ })
}
}

View File

@@ -1,11 +1,39 @@
import { defineConfig } from 'vite'
import { resolve } from 'path'
import fs from 'fs'
const LOG_FILE = resolve(__dirname, 'game-test.log')
export default defineConfig({
server: {
port: 3000,
host: true
host: true,
},
plugins: [
{
name: 'game-logger',
configureServer(server) {
server.middlewares.use('/api/log', (req, res) => {
if (req.method === 'POST') {
let body = ''
req.on('data', chunk => { body += chunk })
req.on('end', () => {
fs.appendFileSync(LOG_FILE, body + '\n', 'utf8')
res.writeHead(200)
res.end('ok')
})
} else if (req.method === 'DELETE') {
fs.writeFileSync(LOG_FILE, '', 'utf8')
res.writeHead(200)
res.end('cleared')
} else {
res.writeHead(405)
res.end()
}
})
},
},
],
build: {
outDir: 'dist',
assetsInlineLimit: 0,