Use a scene as a fullscreen background animation for all other game scenes

I’m working on a puzzle game in Phaser3 and I want to create a blue sky with animated clouds as the background.

So I thought I create a separate scene, put all the animation code there and re-use that scene in all other scenes, so in the MainMenu, LevelSelect, GameScene etc.

The reason I want to do it as a a separate scene, is so I don’t have to add the same functions and animation code to each scene. And also so that the animation doesn’t reset when switching between the MainMenu scene to the GameScene, so it should continue seemlessly when the player changes scenes and starts the game.

So my question is:
how can I use a singleton scene as a background scene for all other scenes?

I know it’s possible in Phaser3 to layer scenes, for example show scenes within a scene. But I couldn’t find a code example of using it as one singleton background scene.

See the example code below, you can copy&paste into the Phaser3 sandbox to run it. It’s a simple example and I’ll add better graphics later, but first I need the code to work properly.

class MyBackground extends Phaser.Scene
{
    constructor ()
    {
        super('MyBackground');
    }

    preload()
    {
        // blue to white
        var clr_g1 = 0x0000ff; //blue
        var clr_g2 = 0xffffff; //white

        // create background graphic: a large rectangle
        let bg_sky = this.add.graphics();
        //bg_sky.fillGradientStyle(clr_g1, clr_g1, clr_g2, clr_g2); // gradient doesn't work with generateTexture?
        bg_sky.fillStyle(0x8080ff);
        bg_sky.fillRect(0, 0, 800, 600);

        // draw random clouds
        for (var i=0; i < 12; i++)
        {
            // random position
            var size  = Phaser.Math.Between(50, 100);
            var xpos = Phaser.Math.Between(size, 800-size);
            var ypos = Phaser.Math.Between(0, 600);


            bg_sky.fillStyle(0xE0E0E0, 1.0);
            bg_sky.fillEllipse(xpos, ypos, 2.0*size, 1.0*size);
            bg_sky.fillStyle(0xFFFFFF, 1.0);
            bg_sky.fillEllipse(xpos, ypos-(0.1*size), 1.6*size, 0.8*size);
        }

        // turn graphics into texture
        bg_sky.generateTexture('background', 800, 600);
        bg_sky.setVisible(false);
    }

    create ()
    {
        this._bg_image = this.add.tileSprite(400, 300, 800, 600, 'background');
        this._bg_off = 0;
    }

    update (time, delta)
    {
        this._bg_off =  this._bg_off + 0.2;
        this._bg_image.setTilePosition(this._bg_off, 0);
    }
}

class MenuScene extends Phaser.Scene
{
    constructor ()
    {
        super('MenuScene');
    }

    create ()
    {
        this.add.text(20, 20, "THIS IS THE MENU-SCENE");
        // Phaser3 has no buttons, create a make-shift button
        var game_btn = this.add.graphics();
        game_btn.fillStyle(0xff0000);
        game_btn.fillRect(100, 300, 300, 50)
        game_btn.setInteractive(new Phaser.Geom.Rectangle(100, 300, 300, 50), Phaser.Geom.Rectangle.Contains);
        game_btn.on('pointerdown', function() { this.scene.start('GameScene') } , this);
        this.add.text(100, 300, "click to switch to game-scene");
    }
}

class GameScene extends Phaser.Scene
{
    constructor ()
    {
        super('GameScene');
    }

    create ()
    {
        this.add.text(400, 20, "THIS IS THE GAME-SCENE");
        // Phaser3 has no buttons, create a make-shift button
        var game_btn = this.add.graphics();
        game_btn.fillStyle(0xff00ff);
        game_btn.fillRect(150, 100, 300, 50)
        game_btn.setInteractive(new Phaser.Geom.Rectangle(150, 100, 300, 50), Phaser.Geom.Rectangle.Contains);
        game_btn.on('pointerdown', function() { this.scene.start('MenuScene') } , this);
        this.add.text(150, 100, "click to switch to menu-scene");
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: [ MyBackground, MenuScene, GameScene ]
};

const game = new Phaser.Game(config);

Since scenes run independently, you can start the background scene once and leave it running (behind the others) while you switch between the other scenes.

1 Like

Thanks.:+1: I’ve worked with Phaser for a while, but I wasn’t quite aware of the different ways to start and swtich scenes.

Btw what is the different between this.scene.start and this.scene.switch ? Functionally they seem to do the same thing :thinking:

I’ve changed my example code to this and it seems to work great:

class BootScene extends Phaser.Scene {
    constructor() {
        super('BootScene');
    }

    preload() {
        // blue to white
        var clr_g1 = 0x0000ff; //blue
        var clr_g2 = 0xffffff; //white

        // create background graphic (do not add; so that it stays invisible and doesn't display for one frame)
        let bg_sky = this.make.graphics({x: 0, y: 0, add: false});
        //bg_sky.fillGradientStyle(clr_g1, clr_g1, clr_g2, clr_g2); // gradient doesn't work with generateTexture?
        bg_sky.fillStyle(0x8080ff);
        bg_sky.fillRect(0, 0, 800, 600);

        // draw random clouds
        for (var i=0; i < 12; i++)
        {
            // random position
            var size  = Phaser.Math.Between(50, 100);
            var xpos = Phaser.Math.Between(size, 800-size);
            var ypos = Phaser.Math.Between(0, 600);


            bg_sky.fillStyle(0xE0E0E0, 1.0);
            bg_sky.fillEllipse(xpos, ypos, 2.0*size, 1.0*size);
            bg_sky.fillStyle(0xFFFFFF, 1.0);
            bg_sky.fillEllipse(xpos, ypos-(0.1*size), 1.6*size, 0.8*size);
        }

        // turn graphics into texture
        bg_sky.generateTexture('background', 800, 600);
    }

    create() {
        this.scene
            .launch('MyBackground')
            .launch('MenuScene')
            .remove();
    }
}

class MyBackground extends Phaser.Scene
{
    constructor ()
    {
        super('MyBackground');
    }

    create ()
    {
        this._bg_image = this.add.tileSprite(400, 300, 800, 600, 'background');
        this._bg_off = 0;
    }

    update (time, delta)
    {
        this._bg_off =  this._bg_off + 0.2;
        this._bg_image.setTilePosition(this._bg_off, 0);
    }
}

class MenuScene extends Phaser.Scene
{
    constructor ()
    {
        super('MenuScene');
    }

    create ()
    {
        this.add.text(20, 20, "THIS IS THE MENU-SCENE");
        // Phaser3 has no buttons, create a make-shift button
        var game_btn = this.add.graphics();
        game_btn.fillStyle(0xff0000);
        game_btn.fillRect(100, 300, 300, 50)
        game_btn.setInteractive(new Phaser.Geom.Rectangle(100, 300, 300, 50), Phaser.Geom.Rectangle.Contains);
        game_btn.on('pointerdown', function() { this.scene.start('GameScene') } , this);
        this.add.text(100, 300, "click to switch to game-scene");
    }
}

class GameScene extends Phaser.Scene
{
    constructor ()
    {
        super('GameScene');
    }

    create ()
    {
        this.add.text(400, 20, "THIS IS THE GAME-SCENE");
        // Phaser3 has no buttons, create a make-shift button
        var game_btn = this.add.graphics();
        game_btn.fillStyle(0xff00ff);
        game_btn.fillRect(150, 100, 300, 50)
        game_btn.setInteractive(new Phaser.Geom.Rectangle(150, 100, 300, 50), Phaser.Geom.Rectangle.Contains);
        game_btn.on('pointerdown', function() { this.scene.start('MenuScene') } , this);
        this.add.text(150, 100, "click to switch to menu-scene");
    }
}

const config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    backgroundColor: '#000000',
    parent: 'phaser-example',
    scene: [ BootScene, MyBackground, MenuScene, GameScene ]
};

const game = new Phaser.Game(config);

start() starts the target scene and stops the calling scene.

switch() starts or wakes the target scene and sleeps the calling scene.

So start moves between 2 scenes in a start/stop pattern, and switch does so in a sleep/wake pattern.

1 Like

Thanks for clarifying :+1:

Also, do you maybe have an idea why the fillGradientStyle doesn’t seem to work with the generateTexture? When I use that the background is just black. Or put another way, I want to be able to randomly select the 2 gradient colors at run-time, so how can I create a texture with a gradient?

generateTexture() uses Canvas 2d rendering, so no gradient fills.