🐛 fix HUD overlay zoom + add red center crosshair

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.
This commit is contained in:
2026-03-21 11:34:04 +00:00
parent 7c130763b5
commit a93e8a2c5d

View File

@@ -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)
}
}
@@ -148,7 +151,6 @@ export class ZoomTestScene extends Phaser.Scene {
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',
@@ -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
@@ -231,26 +260,16 @@ 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 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 lines = [