class Game extends Phaser.Scene {
    constructor() {
        super('game');
        this.map = null;
    }

    preload(){
        this.load.image('tiles', 'https://assets.codepen.io/8692994/spritesheet.png');
        this.load.tilemapTiledJSON('map', 'https://assets.codepen.io/8692994/map.json');
    }

    update() {}

    create() {
        this.map = this.make.tilemap({ key: 'map' });
        const tileset = this.map.addTilesetImage('spritesheet', 'tiles');

        const layers = this.map.layers.map(layerData => {
            return this.map.createLayer(layerData.name, tileset);
        });

        this.registry.set('layers', layers);

        // Set the world size based on the map dimensions
        this.worldWidth = this.map.widthInPixels;
        this.worldHeight = this.map.heightInPixels;

        // Set the world bounds
        this.physics.world.setBounds(0, 0, this.worldWidth, this.worldHeight);

        // Enable input for the camera
        this.input.setTopOnly(true);
        
        // Initialize the zoom state
        this.zoomState = {minZoom: 0.5, maxZoom: 2.5, zoomStep: 0.06};

        // Initialize the drag state
        this.dragState = {active: false, prevPointer: null};

        // Setup pointer events
        this.input.on('wheel', (pointer, gameObjects, deltaX, deltaY, deltaZ) => {
            this.handleZoom(pointer, deltaX, deltaY)
        });
 
        this.input.on('pointerdown', (pointer) => {
            this.dragState.active = true;
            this.dragState.prevPointer = { x: pointer.x, y: pointer.y };
        });

        this.input.on('pointermove', (pointer) => {
            this.handlePan(pointer)
        });

        this.input.on('pointerup', (pointer) => {
					this.dragState.active = false
        });


			this.updateCameraBounds();
    }

    handleZoom(pointer, deltaX, deltaY) {
        const zoomDirection = deltaY > 0 ? -1 : 1;

        let newZoom = Phaser.Math.Clamp(
            this.cameras.main.zoom + zoomDirection * this.zoomState.zoomStep,
            this.zoomState.minZoom,
            this.zoomState.maxZoom
        );

        this.cameras.main
        .setZoom(newZoom)
        .centerOn(pointer.worldX, pointer.worldY);

        // this.cameras.main.setZoom(newZoom);
    }

    handlePan(pointer){
        if (this.dragState.active) {
            const deltaX = this.dragState.prevPointer.x - pointer.x;
            const deltaY = this.dragState.prevPointer.y - pointer.y;

            this.cameras.main.scrollX += deltaX;
            this.cameras.main.scrollY += deltaY;

            this.dragState.prevPointer = { x: pointer.x, y: pointer.y };
        }
    }

    updateCameraBounds() {
        const canvasWidth = this.scale.width;
        const canvasHeight = this.scale.height;
      
        this.cameras.main.setBounds(
          0, 
          0, 
          Math.max(this.worldWidth, canvasWidth), 
          Math.max(this.worldHeight, canvasHeight)
        );
    }
}

const config = {
  parent: "phaser-example",
	type: Phaser.AUTO,
	pixelArt: true,
	scene: [Game],
	physics: {
		default: 'arcade',
		arcade: {
			gravity: { y: 0 },
			debug: true
		}
	},
	scale: {
		width: window.innerWidth * window.devicePixelRatio,
		height: window.innerHeight * window.devicePixelRatio,
	},
};

const game = new Phaser.Game(config);
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/phaser/3.55.2/phaser.min.js