Issue #5: Mouse handling — zoom-to-mouse + middle-click pan #10
@@ -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 = [
|
||||
|
||||
Reference in New Issue
Block a user