diff --git a/src/systems/CameraSystem.ts b/src/systems/CameraSystem.ts index 3eeea17..4a7ec2c 100644 --- a/src/systems/CameraSystem.ts +++ b/src/systems/CameraSystem.ts @@ -23,6 +23,9 @@ export class CameraSystem { } private saveTimer = 0 private readonly SAVE_TICK = 2000 + private middlePanActive = false + private lastPanX = 0 + private lastPanY = 0 constructor(scene: Phaser.Scene, adapter: LocalAdapter) { this.scene = scene @@ -49,10 +52,48 @@ 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 zoom = Phaser.Math.Clamp(cam.zoom - Math.sign(dy) * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM) - cam.setZoom(zoom) + // Scroll wheel zoom — zoom toward mouse pointer position + this.scene.input.on('wheel', (ptr: Phaser.Input.Pointer, _objs: unknown, _dx: number, dy: number) => { + const oldZoom = cam.zoom + const newZoom = Phaser.Math.Clamp(oldZoom - Math.sign(dy) * ZOOM_STEP, MIN_ZOOM, MAX_ZOOM) + if (newZoom === oldZoom) return + + // World point under mouse before zoom + const worldX = cam.scrollX + ptr.x / oldZoom + const worldY = cam.scrollY + ptr.y / oldZoom + + cam.setZoom(newZoom) + + // Adjust scroll so the same world point stays under the mouse + cam.scrollX = worldX - ptr.x / newZoom + cam.scrollY = worldY - ptr.y / newZoom + }) + + // Middle-click pan: start on button down + this.scene.input.on('pointerdown', (ptr: Phaser.Input.Pointer) => { + if (ptr.middleButtonDown()) { + this.middlePanActive = true + this.lastPanX = ptr.x + this.lastPanY = ptr.y + } + }) + + // Middle-click pan: move camera while held + this.scene.input.on('pointermove', (ptr: Phaser.Input.Pointer) => { + if (!this.middlePanActive) return + const dx = (ptr.x - this.lastPanX) / cam.zoom + const dy = (ptr.y - this.lastPanY) / cam.zoom + cam.scrollX -= dx + cam.scrollY -= dy + this.lastPanX = ptr.x + this.lastPanY = ptr.y + }) + + // Middle-click pan: stop on button release + this.scene.input.on('pointerup', (ptr: Phaser.Input.Pointer) => { + if (this.middlePanActive && !ptr.middleButtonDown()) { + this.middlePanActive = false + } }) }