From faa4deb0bff1176b4b12f8393ad3d7491568343d Mon Sep 17 00:00:00 2001 From: tekki mariani Date: Sat, 21 Mar 2026 11:34:04 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=90=9B=20fix=20HUD=20overlay=20zoom=20+?= =?UTF-8?q?=20add=20red=20center=20crosshair?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Text overlay now uses a dedicated HUD camera (zoom=1, fixed scroll) so it's never scaled by the world zoom. World objects and HUD objects are separated via camera ignore lists. Added red screen-center crosshair to HUD layer as a precise alignment reference. --- src/test/ZoomTestScene.ts | 115 ++++++++++++++++++++++---------------- 1 file changed, 67 insertions(+), 48 deletions(-) diff --git a/src/test/ZoomTestScene.ts b/src/test/ZoomTestScene.ts index f848663..2008daa 100644 --- a/src/test/ZoomTestScene.ts +++ b/src/test/ZoomTestScene.ts @@ -1,14 +1,14 @@ 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 SNAPSHOT_EVERY = 2000 // ms between periodic log snapshots +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. @@ -19,6 +19,9 @@ const SNAPSHOT_EVERY = 2000 // ms between periodic log snapshots */ export class ZoomTestScene extends Phaser.Scene { private logText!: Phaser.GameObjects.Text + private hudCamera!: Phaser.Cameras.Scene2D.Camera + private worldObjects: Phaser.GameObjects.GameObject[] = [] + private hudObjects: Phaser.GameObjects.GameObject[] = [] private keys!: { up: Phaser.Input.Keyboard.Key down: Phaser.Input.Keyboard.Key @@ -43,19 +46,18 @@ export class ZoomTestScene extends Phaser.Scene { this.drawGrid() this.setupCamera() this.setupInput() - this.createOverlay() + this.createHUD() + this.setupCameras() } /** * Draws the static world grid into world space. - * - Faint tile lines on every tile boundary - * - Small green crosshairs every MARKER_EVERY tiles - * - Yellow labeled crosshairs every LABEL_EVERY tiles - * - Red world border + * All objects are registered in worldObjects for HUD-camera exclusion. */ private drawGrid(): void { const worldPx = GRID_TILES * TILE_SIZE const g = this.add.graphics() + this.worldObjects.push(g) // Background fill g.fillStyle(0x111811) @@ -90,12 +92,13 @@ export class ZoomTestScene extends Phaser.Scene { // Coordinate labels at LABEL_EVERY intersections for (let tx = 0; tx <= GRID_TILES; tx += LABEL_EVERY) { for (let ty = 0; ty <= GRID_TILES; ty += LABEL_EVERY) { - this.add.text( + const label = this.add.text( tx * TILE_SIZE + 4, ty * TILE_SIZE + 4, `${tx},${ty}`, { fontSize: '9px', color: '#ffff88', fontFamily: 'monospace' } ).setDepth(1) + this.worldObjects.push(label) } } @@ -141,27 +144,26 @@ export class ZoomTestScene extends Phaser.Scene { _dx: number, dy: number ) => { - const zoomBefore = cam.zoom + 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), + 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) }, + 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), @@ -176,9 +178,25 @@ export class ZoomTestScene extends Phaser.Scene { } /** - * Creates the fixed HUD text overlay (scroll factor 0 = screen space). + * Creates all HUD elements: log overlay and screen-center crosshair. + * All objects are registered in hudObjects for main-camera exclusion. + * Uses a dedicated HUD camera (zoom=1, fixed) so elements are never scaled. */ - private createOverlay(): void { + private createHUD(): void { + const w = this.scale.width + const h = this.scale.height + + // Screen-center crosshair (red) + const cross = this.add.graphics() + const arm = 16 + cross.lineStyle(1, 0xff2222, 0.9) + cross.lineBetween(w / 2 - arm, h / 2, w / 2 + arm, h / 2) + cross.lineBetween(w / 2, h / 2 - arm, w / 2, h / 2 + arm) + cross.fillStyle(0xff2222, 1.0) + cross.fillCircle(w / 2, h / 2, 2) + this.hudObjects.push(cross) + + // Log text overlay this.logText = this.add.text(10, 10, '', { fontSize: '13px', color: '#e8e8e8', @@ -186,16 +204,27 @@ export class ZoomTestScene extends Phaser.Scene { padding: { x: 10, y: 8 }, lineSpacing: 3, fontFamily: 'monospace', - }) - .setScrollFactor(0) - .setDepth(100) + }).setDepth(100) + this.hudObjects.push(this.logText) + } + + /** + * Adds a dedicated HUD camera (zoom=1, no scroll) and separates + * world objects from HUD objects so neither camera renders both layers. + */ + private setupCameras(): void { + this.hudCamera = this.cameras.add(0, 0, this.scale.width, this.scale.height) + this.hudCamera.setScroll(0, 0) + this.hudCamera.setZoom(1) + + this.cameras.main.ignore(this.hudObjects) + this.hudCamera.ignore(this.worldObjects) } update(_time: number, delta: number): void { this.handleKeyboard(delta) this.updateOverlay() - // Periodic snapshot this.snapshotTimer += delta if (this.snapshotTimer >= SNAPSHOT_EVERY) { this.snapshotTimer = 0 @@ -208,8 +237,8 @@ export class ZoomTestScene extends Phaser.Scene { * @param delta - Frame delta in milliseconds */ private handleKeyboard(delta: number): void { - const cam = this.cameras.main - const speed = CAMERA_SPEED * (delta / 1000) / cam.zoom + const cam = this.cameras.main + const speed = CAMERA_SPEED * (delta / 1000) / cam.zoom const worldPx = GRID_TILES * TILE_SIZE let dx = 0, dy = 0 @@ -231,27 +260,17 @@ export class ZoomTestScene extends Phaser.Scene { const cam = this.cameras.main const ptr = this.input.activePointer - // Viewport size in world pixels (what is actually visible) - const vpWidthPx = cam.width / cam.zoom - const vpHeightPx = cam.height / cam.zoom - - // Viewport size in tiles + const vpWidthPx = cam.width / cam.zoom + const vpHeightPx = cam.height / cam.zoom const vpWidthTiles = vpWidthPx / TILE_SIZE const vpHeightTiles = vpHeightPx / TILE_SIZE - - // Camera center in world coords - const centerWorldX = cam.scrollX + vpWidthPx / 2 - const centerWorldY = cam.scrollY + vpHeightPx / 2 - - // Tile under mouse - const mouseTileX = Math.floor(ptr.worldX / TILE_SIZE) - const mouseTileY = Math.floor(ptr.worldY / TILE_SIZE) - - // Tile at camera center - const centerTileX = Math.floor(centerWorldX / TILE_SIZE) - const centerTileY = Math.floor(centerWorldY / TILE_SIZE) - - const renderer = this.game.renderer.type === Phaser.WEBGL ? 'WebGL' : 'Canvas' + const centerWorldX = cam.scrollX + vpWidthPx / 2 + const centerWorldY = cam.scrollY + vpHeightPx / 2 + const mouseTileX = Math.floor(ptr.worldX / TILE_SIZE) + const mouseTileY = Math.floor(ptr.worldY / TILE_SIZE) + const centerTileX = Math.floor(centerWorldX / TILE_SIZE) + const centerTileY = Math.floor(centerWorldY / TILE_SIZE) + const renderer = this.game.renderer.type === Phaser.WEBGL ? 'WebGL' : 'Canvas' const lines = [ '── ZOOM TEST [Phaser default] ──',