Issue #5: Mouse handling — zoom-to-mouse + middle-click pan #10
@@ -27,11 +27,19 @@ export class CameraSystem {
|
||||
private lastPanX = 0
|
||||
private lastPanY = 0
|
||||
|
||||
/**
|
||||
* @param scene - The Phaser scene this system belongs to
|
||||
* @param adapter - Network adapter used to persist camera position
|
||||
*/
|
||||
constructor(scene: Phaser.Scene, adapter: LocalAdapter) {
|
||||
this.scene = scene
|
||||
this.adapter = adapter
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the camera: restores saved position, registers keyboard keys,
|
||||
* sets up scroll-wheel zoom-to-mouse, and middle-click pan.
|
||||
*/
|
||||
create(): void {
|
||||
const state = stateManager.getState()
|
||||
const cam = this.scene.cameras.main
|
||||
@@ -52,10 +60,22 @@ export class CameraSystem {
|
||||
d: kb.addKey(Phaser.Input.Keyboard.KeyCodes.D),
|
||||
}
|
||||
|
||||
// Scroll wheel zoom
|
||||
this.scene.input.on('wheel', (_ptr: Phaser.Input.Pointer, _objs: unknown, _dx: number, dy: number) => {
|
||||
const newZoom = Phaser.Math.Clamp(cam.zoom - Math.sign(dy) * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
|
||||
// Scroll wheel: zoom-to-mouse.
|
||||
// Phaser zooms from the screen center, so the world point under the mouse
|
||||
// is corrected by shifting scroll by the mouse offset from center.
|
||||
this.scene.input.on('wheel', (ptr: Phaser.Input.Pointer, _objs: unknown, _dx: number, dy: number) => {
|
||||
const zoomBefore = cam.zoom
|
||||
const newZoom = Phaser.Math.Clamp(zoomBefore - Math.sign(dy) * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM)
|
||||
cam.setZoom(newZoom)
|
||||
|
||||
const factor = 1 / zoomBefore - 1 / newZoom
|
||||
cam.scrollX += (ptr.x - cam.width / 2) * factor
|
||||
cam.scrollY += (ptr.y - cam.height / 2) * factor
|
||||
|
||||
const worldW = WORLD_TILES * 32
|
||||
const worldH = WORLD_TILES * 32
|
||||
cam.scrollX = Phaser.Math.Clamp(cam.scrollX, 0, worldW - cam.width / newZoom)
|
||||
cam.scrollY = Phaser.Math.Clamp(cam.scrollY, 0, worldH - cam.height / newZoom)
|
||||
})
|
||||
|
||||
// Middle-click pan: start on button down
|
||||
@@ -86,6 +106,10 @@ export class CameraSystem {
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the camera via keyboard input and periodically saves the position.
|
||||
* @param delta - Frame delta in milliseconds
|
||||
*/
|
||||
update(delta: number): void {
|
||||
const cam = this.scene.cameras.main
|
||||
const speed = CAMERA_SPEED * (delta / 1000) / cam.zoom
|
||||
@@ -103,7 +127,7 @@ export class CameraSystem {
|
||||
|
||||
if (dx !== 0 && dy !== 0) { dx *= 0.707; dy *= 0.707 }
|
||||
|
||||
const worldW = WORLD_TILES * 32 // TILE_SIZE hardcoded since WORLD_PX may not exist
|
||||
const worldW = WORLD_TILES * 32
|
||||
const worldH = WORLD_TILES * 32
|
||||
cam.scrollX = Phaser.Math.Clamp(cam.scrollX + dx, 0, worldW - cam.width / cam.zoom)
|
||||
cam.scrollY = Phaser.Math.Clamp(cam.scrollY + dy, 0, worldH - cam.height / cam.zoom)
|
||||
@@ -120,14 +144,24 @@ export class CameraSystem {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the world coordinates of the visual camera center.
|
||||
* Phaser zooms from the screen center, so the center world point
|
||||
* is scrollX + screenWidth/2 (independent of zoom level).
|
||||
* @returns World position of the screen center
|
||||
*/
|
||||
getCenterWorld(): { x: number; y: number } {
|
||||
const cam = this.scene.cameras.main
|
||||
return {
|
||||
x: cam.scrollX + cam.width / (2 * cam.zoom),
|
||||
y: cam.scrollY + cam.height / (2 * cam.zoom),
|
||||
x: cam.scrollX + cam.width / 2,
|
||||
y: cam.scrollY + cam.height / 2,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile coordinates of the visual camera center.
|
||||
* @returns Tile position (integer) of the screen center
|
||||
*/
|
||||
getCenterTile(): { tileX: number; tileY: number } {
|
||||
const { x, y } = this.getCenterWorld()
|
||||
return { tileX: Math.floor(x / 32), tileY: Math.floor(y / 32) }
|
||||
|
||||
Reference in New Issue
Block a user