Use camera mask as transition at start of scene

I’m trying to create a scene transition effect, the previous scene fades to black and the next scene will start with a camera mask using either a shape or a bitmap (whichever performs fastest). Is this possible in Phaser 3?

Basically, I’m trying to recreate the effect used in New Super Mario Bros when a level starts, see image below. Btw NSMB also does a bit transparency but ideally it should just be a black border.

I’ve tried the code below in the sandbox, but it seems a mask doesn’t have a scale or setScale property or method? See code below (btw is there a way to save&share sandbox code?)

var config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
    scene: {
        preload: preload,
        create: create,
        update: update
    }
};

var controls;

var game = new Phaser.Game(config);

function preload ()
{
    this.load.image('phaser2', 'assets/sprites/phaser2.png');

    this.load.image('backdrop', 'assets/pics/platformer-backdrop.png');
    this.load.image('dude', 'assets/sprites/phaser-dude.png');

    this.load.bitmapFont('atari', 'assets/fonts/bitmap/atari-smooth.png', 'assets/fonts/bitmap/atari-smooth.xml');
}

function create ()
{

    var bg = this.add.image(400, 300, 'backdrop').setScale(2.0, 2.0);
    var dude = this.add.image(400, 300, 'dude').setScale(2.0, 2.0);

    var text = this.add.bitmapText(400, 300, 'atari', '', 38).setOrigin(0.5).setCenterAlign().setInteractive();

    text.setText('Mission 1\nGet ready!');
    text.setText('Too bad!\nGame Over');

	
    var shape1 = this.make.graphics().fillStyle(0x00ff00).fillCircle(400, 300, 200);
    var bitmap1 = this.add.image(400, 300, 'phaser2');

    var mask1 = shape1.createGeometryMask();
    //var mask2 = bitmap1.createBitmapMask();

    this.cameras.main.setMask(mask1);
	//this.cameras.main.setMask(mask2);

    this.tweens.add({
        targets: mask1,
        scale: 3.0,
        ease: 'Sine.easeInOut',
        yoyo: true,
        repeat: -1,
        duration: 2000
    });
}

function update (time, delta)
{
    // controls.update(delta);
}

I’ve been playing around and I think I’ve got something. You can set the mask to be a graphics object and then in the update() function continuously redraw the graphics object to make the opening bigger or smaller. The code below transitions with a star or a circle. Not sure if this will perform well on a slower mobile, because of the many redraws.

You can copy&paste the code below into the phaser 3 sandbox.:slight_smile:

// main game logic object
var TestScene = new Phaser.Class({

    Extends: Phaser.Scene,

    initialize:

    function GameScene ()
    {
        Phaser.Scene.call(this, { key: "gamescene" });
    },

    preload: function ()
    {
		this.load.image('phaser2', 'assets/sprites/phaser2.png');

		this.load.image('backdrop', 'assets/pics/platformer-backdrop.png');
		this.load.image('dude', 'assets/sprites/phaser-dude.png');

		this.load.bitmapFont('atari', 'assets/fonts/bitmap/atari-smooth.png', 'assets/fonts/bitmap/atari-smooth.xml');
    },

    create: function ()
    {
		var bg = this.add.image(400, 300, 'backdrop').setScale(2.5, 2.5);

		var text = this.add.bitmapText(400, 300, 'atari', '', 38).setOrigin(0.5).setCenterAlign().setInteractive();

		text.setText('Mission 1\nGet ready!');

        for (var i = 0; i < 10; i++) {
            var x = Phaser.Math.RND.between(10, this.cameras.main.width-10);
            var y = Phaser.Math.RND.between(10, this.cameras.main.width-10);
		    this.add.image(x, y, 'dude'); // .setScale(2.0, 2.0);
        };


		this.shape1 = this.make.graphics().fillStyle(0x00ff00).fillCircle(400, 300, 100);

		var mask1 = this.shape1.createGeometryMask();

		this.cameras.main.setMask(mask1);

		this.transition = {"counter": 0};
        
		this.tweens.add({
			targets: this.transition,
			counter: 500,
			callbackScope: this,
			onUpdate: this.updateTransition,
			ease: 'Sine.easeInOut',
			yoyo: true,
			repeat: -1,
			duration: 2000
		});
	},

    update: function (time, delta)
    {
	},
	
    updateTransition: function (a, b, c, d)
    {
		// controls.update(delta);
		console.log("updateTransition");
		//this.shape1.clear();
        //this.shape1.fillStyle(0x00ff00).fillCircle(400, 300, this.transition.counter);
		var outerRadius = this.transition.counter * 2;
		var innerRadius = this.transition.counter;
		var cx = 400;
		var cy = 300;
		
		this.shape1.clear();
		var rot = Math.PI / 2 * 3;
		var x = cx;
		var y = cy;
		var step = Math.PI / 5;
		//this.shape1.lineStyle(10, 0xffffff, 1.0);
		this.shape1.fillStyle(0xffffff, 1.0);
		this.shape1.beginPath();
		this.shape1.moveTo(cx, cy - outerRadius);
		for (i = 0; i < 5; i++) {
			x = cx + Math.cos(rot) * outerRadius;
			y = cy + Math.sin(rot) * outerRadius;
			this.shape1.lineTo(x, y);
			rot += step;

			x = cx + Math.cos(rot) * innerRadius;
			y = cy + Math.sin(rot) * innerRadius;
			this.shape1.lineTo(x, y);
			rot += step;
		}
		this.shape1.lineTo(cx, cy - outerRadius);
		this.shape1.closePath();
		this.shape1.fillPath();
		this.shape1.strokePath();
	}
});


var config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
	scene: [ TestScene ]
};

var game = new Phaser.Game(config);

And you can do the same with a bitmap, it looks really cool :sunglasses: It’s exactly like the transition effect in New Super Mario Bros.

// main game logic object
var TestScene = new Phaser.Class({

    Extends: Phaser.Scene,

    initialize:

    function GameScene ()
    {
        Phaser.Scene.call(this, { key: "gamescene" });
    },

    preload: function ()
    {
		this.load.image('phaser2', 'assets/sprites/phaser2.png');

		this.load.image('backdrop', 'assets/pics/platformer-backdrop.png');
		this.load.image('dude', 'assets/sprites/phaser-dude.png');

		this.load.bitmapFont('atari', 'assets/fonts/bitmap/atari-smooth.png', 'assets/fonts/bitmap/atari-smooth.xml');
    },

    create: function ()
    {
        // create some background with text and some sprites
		var bg = this.add.image(400, 300, 'backdrop').setScale(2.5, 2.5);

		var text = this.add.bitmapText(400, 300, 'atari', '', 38).setOrigin(0.5).setCenterAlign().setInteractive();
		text.setText('Mission 1\nGet ready!');

        for (var i = 0; i < 10; i++) {
            var x = Phaser.Math.RND.between(10, this.cameras.main.width-10);
            var y = Phaser.Math.RND.between(10, this.cameras.main.width-10);
		    this.add.image(x, y, 'dude'); // .setScale(2.0, 2.0);
        };

        // the logo to use as a maskm, 
		this.logo = this.add.image(400, 300, 'phaser2').setVisible(false);

		this.shape1 = this.make.graphics();
		var mask1 = this.shape1.createBitmapMask(this.logo);

		this.cameras.main.setMask(mask1);

        // create object for use with tween
		this.transition = {"counter": 0};

        // tween counter variable and set onUpdate function
		this.tweens.add({
			targets: this.transition,
			counter: 4.8,
			callbackScope: this,
			onUpdate: this.updateTransition,
			ease: 'Sine.easeInOut',
			yoyo: true,
			repeat: -1,
			duration: 2000
		});
	},

    update: function (time, delta)
    {
	},
	
    updateTransition: function ()
    {
        // this function is called when tweened value updates
		console.log("updateTransition");

        // scale logo
        this.logo.setScale(this.transition.counter);
	}
});


var config = {
    type: Phaser.WEBGL,
    width: 800,
    height: 600,
    backgroundColor: '#2d2d2d',
    parent: 'phaser-example',
	scene: [ TestScene ]
};

var game = new Phaser.Game(config);