Add zoom and drag to move camera
This commit is contained in:
@@ -96,6 +96,9 @@ export const GAME_CONFIG = {
|
|||||||
rendering: {
|
rendering: {
|
||||||
tileSize: 16,
|
tileSize: 16,
|
||||||
cameraZoom: 2,
|
cameraZoom: 2,
|
||||||
|
minZoom: 0.5,
|
||||||
|
maxZoom: 4,
|
||||||
|
zoomStep: 0.1,
|
||||||
wallColor: 0x2b2b2b,
|
wallColor: 0x2b2b2b,
|
||||||
floorColor: 0x161616,
|
floorColor: 0x161616,
|
||||||
exitColor: 0xffd166,
|
exitColor: 0xffd166,
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
private playerPath: Vec2[] = [];
|
private playerPath: Vec2[] = [];
|
||||||
private awaitingPlayer = false;
|
private awaitingPlayer = false;
|
||||||
|
private followPlayer = true;
|
||||||
|
|
||||||
// Sub-systems
|
// Sub-systems
|
||||||
private dungeonRenderer!: DungeonRenderer;
|
private dungeonRenderer!: DungeonRenderer;
|
||||||
@@ -144,8 +145,66 @@ export class GameScene extends Phaser.Scene {
|
|||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Zoom Control
|
||||||
|
this.input.on(
|
||||||
|
"wheel",
|
||||||
|
(
|
||||||
|
_pointer: Phaser.Input.Pointer,
|
||||||
|
_gameObjects: any,
|
||||||
|
_deltaX: number,
|
||||||
|
deltaY: number,
|
||||||
|
_deltaZ: number
|
||||||
|
) => {
|
||||||
|
if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
|
||||||
|
|
||||||
|
const zoomDir = deltaY > 0 ? -1 : 1;
|
||||||
|
const newZoom = Phaser.Math.Clamp(
|
||||||
|
this.cameras.main.zoom + zoomDir * GAME_CONFIG.rendering.zoomStep,
|
||||||
|
GAME_CONFIG.rendering.minZoom,
|
||||||
|
GAME_CONFIG.rendering.maxZoom
|
||||||
|
);
|
||||||
|
this.cameras.main.setZoom(newZoom);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Disable context menu for right-click panning
|
||||||
|
this.input.mouse?.disableContextMenu();
|
||||||
|
|
||||||
|
// Camera Panning
|
||||||
|
this.input.on("pointermove", (p: Phaser.Input.Pointer) => {
|
||||||
|
if (!p.isDown) return;
|
||||||
|
if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
|
||||||
|
|
||||||
|
// Pan with Middle Click or Right Click
|
||||||
|
// Note: p.button is not always reliable in move events for holding,
|
||||||
|
// so we use specific button down checks or the shift key modifier.
|
||||||
|
const isRightDrag = p.rightButtonDown();
|
||||||
|
const isMiddleDrag = p.middleButtonDown();
|
||||||
|
const isShiftDrag = p.isDown && p.event.shiftKey;
|
||||||
|
|
||||||
|
if (isRightDrag || isMiddleDrag || isShiftDrag) {
|
||||||
|
const { x, y } = p.position;
|
||||||
|
const { x: prevX, y: prevY } = p.prevPosition;
|
||||||
|
|
||||||
|
const dx = (x - prevX) / this.cameras.main.zoom;
|
||||||
|
const dy = (y - prevY) / this.cameras.main.zoom;
|
||||||
|
|
||||||
|
this.cameras.main.scrollX -= dx;
|
||||||
|
this.cameras.main.scrollY -= dy;
|
||||||
|
|
||||||
|
this.followPlayer = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Mouse click -> compute path (only during player turn, and not while menu/minimap is open)
|
// Mouse click -> compute path (only during player turn, and not while menu/minimap is open)
|
||||||
this.input.on("pointerdown", (p: Phaser.Input.Pointer) => {
|
this.input.on("pointerdown", (p: Phaser.Input.Pointer) => {
|
||||||
|
// Only allow Left Click (0) for movement
|
||||||
|
if (p.button !== 0) return;
|
||||||
|
|
||||||
|
this.followPlayer = true;
|
||||||
|
|
||||||
|
|
||||||
if (!this.awaitingPlayer) return;
|
if (!this.awaitingPlayer) return;
|
||||||
if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
|
if (this.isMenuOpen || this.isInventoryOpen || this.dungeonRenderer.isMinimapVisible()) return;
|
||||||
|
|
||||||
@@ -258,6 +317,7 @@ export class GameScene extends Phaser.Scene {
|
|||||||
|
|
||||||
private commitPlayerAction(action: Action) {
|
private commitPlayerAction(action: Action) {
|
||||||
this.awaitingPlayer = false;
|
this.awaitingPlayer = false;
|
||||||
|
this.followPlayer = true;
|
||||||
|
|
||||||
const playerEvents = applyAction(this.world, this.playerId, action, this.entityManager);
|
const playerEvents = applyAction(this.world, this.playerId, action, this.entityManager);
|
||||||
const enemyStep = stepUntilPlayerTurn(this.world, this.playerId, this.entityManager);
|
const enemyStep = stepUntilPlayerTurn(this.world, this.playerId, this.entityManager);
|
||||||
@@ -328,13 +388,16 @@ export class GameScene extends Phaser.Scene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.dungeonRenderer.computeFov(this.playerId);
|
this.dungeonRenderer.computeFov(this.playerId);
|
||||||
|
if (this.followPlayer) {
|
||||||
this.centerCameraOnPlayer();
|
this.centerCameraOnPlayer();
|
||||||
|
}
|
||||||
this.dungeonRenderer.render(this.playerPath);
|
this.dungeonRenderer.render(this.playerPath);
|
||||||
this.emitUIUpdate();
|
this.emitUIUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadFloor(floor: number) {
|
private loadFloor(floor: number) {
|
||||||
this.floorIndex = floor;
|
this.floorIndex = floor;
|
||||||
|
this.followPlayer = true;
|
||||||
|
|
||||||
const { world, playerId } = generateWorld(floor, this.runState);
|
const { world, playerId } = generateWorld(floor, this.runState);
|
||||||
this.world = world;
|
this.world = world;
|
||||||
|
|||||||
Reference in New Issue
Block a user