Camera bounds with container overflow

Hi everyone,

I’m a webdev trying to make a small game on my freetime.
I find Phaser pretty neat until now, but I would like some advice on how to handle multiple resolutions and scrolling.

Basically what I would like to achieve is the following :

  • The game view must fit in a container (maybe smaller than the browser window) of any size and still cover it all
  • The aspect ratio must be maintained
  • The camera must follow the player and stop scrolling when the world’s bounds are reached

The world has a fixed size that never changes (pretty small).

The framework seems to already have everything required to do what I discribed.

For the game config I use:

{
	width: 1280,
    height: 700,
    scale: {
    	mode: Phaser.Scale.ENVELOP
	},
	...
}

Then for the camera:

this.cameras.main.startFollow(this.player);
this.cameras.main.setBounds(0, 0, this.gridSizeX * this.gridCellSize, this.gridSizeY * this.gridCellSize);

Very basic stuff, but the scale mode make the game area overflow its container.
That’s exactly what I want but the camera bounds become incorrect as demonstrated here:

Do you know a way to setup the camera to scroll until the end of the overflowing area ?

Thanks a lot!

I’ve finally found a workaround. I share it in the hope it could help someone else.

I think the scaleManager is bugged, so I’ve set the scale mode to Phaser.Scale.NONE and do it manually.

I’ve created a GameObject named CanvasScaler, that I add to my scene like this:

export default class PlayScene extends Scene {
    [...]
    create () {
        const scaler = new CanvasScaler(this, 'CanvasScaler');
        this.add.existing(scaler);
    }
}

The CanvasScaler listen for the resize event of the ScaleManager and re-calculate the size of the canvas and the position and size of the camera :

import * as Phaser from 'phaser';
import { proxy } from "essentials/utils/utils";

export class CanvasScaler extends Phaser.GameObjects.GameObject {
    public constructor(scene: Phaser.Scene, type: string) {
        super(scene, type);
        this.bind();
    }

    private bind(): void {
        this.scene.scale.on('resize', proxy(this.resizeCanvas, this));
        this.resizeCanvas();
    }

    private resizeCanvas() {
        const containerWidth = this.scene.scale.canvas.parentElement.offsetWidth + 1; // + 1 to ensure the size of the camera is not 1px short after rounding
        const containerHeight = this.scene.scale.canvas.parentElement.offsetHeight + 1; // same

        const gameWidth = this.scene.scale.gameSize.width;
        const gameHeight = this.scene.scale.gameSize.height;
        const gameRatio = gameWidth / gameHeight;

        let newWidth = containerWidth;
        let newHeight = containerWidth / gameRatio;

        if (newHeight < containerHeight) {
            newHeight = containerHeight;
            newWidth = containerHeight * gameRatio;
        }
        const marginTop = (containerHeight - newHeight) / 2;
        const marginLeft = (containerWidth - newWidth) / 2;

        // Scale the canvas
        this.scene.scale.canvas.style.width = Math.round(newWidth) + 'px';
        this.scene.scale.canvas.style.height = Math.round(newHeight) + 'px';

        // Center it into view
        this.scene.scale.canvas.style.marginTop = Math.round(marginTop) + 'px';
        this.scene.scale.canvas.style.marginLeft = Math.round(marginLeft) + 'px';

        const scaleRatioX = marginTop ? containerWidth / gameWidth : 1;
        const scaleRatioY = marginLeft ? containerHeight / gameHeight : 1;
        const offsetX = Math.floor((-marginLeft) / scaleRatioY);
        const offsetY = Math.floor((-marginTop) / scaleRatioX);

        // Update the camera size and position
        this.scene.cameras.main.setViewport(
            offsetX,
            offsetY,
            marginLeft ? (gameWidth - (offsetX * 2)) : gameWidth,
            marginTop ? (gameHeight - (offsetY * 2)) : gameHeight
        );
    }
}

Here is the result:

enter image description here

The camera’s setBounds and startFollow stay the same.