Custom Phaser.Container stats panel with score/levelnr, how to destroy or check if it still exists?

I want to add a custom panel at the start of a game level and also during the game or at the end with some stats and info. I’ve created a custom Phaser.Container class object, which displays a text, bounces into the sceen, then waits short while before moving itself off-screen with another tween animation.

This all technically works and displays correctly. But depending on how people play the game, the infopanel could appear a lot of times.

So I’m concerned that old un-used infopanel variables are left-over and still active in the background. This would take up memory (and possible cpu time) but in any case this shouldn’t happen.

My question is

  1. How can monitor any custom objects still being active? I tried console.log in the update method of the object, but that doesn’t seem to fire(?) is that correct?
  2. How can I properly destroy or remove the custom objects once they are done displaying?

See below for a code example, you can copy&paste it into the Phaser3 sandbox

class InfoPanel extends Phaser.GameObjects.Container
{
    constructor (scene)
    {
        super(scene)

		this._scene = scene;

        // dark background panel
        this._background = scene.add.graphics(0, 0);
		this._background.fillStyle(0x000000, 0.4);
		this._background.fillRect(0, 0, 800, 80);

        this._infotext = scene.add.bitmapText(400, 32, 'atari', "test123", 24).setOrigin(0.5);
		this._txt = "";

        this.add(this._background);
        this.add(this._infotext);

		// container to scene
		scene.add.existing(this);
    }

    display (txt)
    {
        console.log("InfoPanel -- display txt=" + txt);
        this._infotext.setText(txt);
		this._txt = txt;

		// set infopanel outside screen
		this.y = 600 + 80;

        // tween infopanel into the screen
        this._info_tw = this._scene.tweens.add( {
            targets: this,
            y: 250,
            duration: 800,
            hold: 1000, // wait 1sec before removing
            ease: 'bounce.out',
			onComplete: this.removePanel.bind(this)
        });
    }

    removePanel ()
    {
        // turn cursor bounce to indicate
        this._info_tw = this._scene.tweens.add({
            targets: this,
            y: -100,
            duration: 800,
            hold: 500,
            ease: 'bounce.in'
        });
	}

    update (time, delta)
    {
        console.log("InfoPanel(" + this._txt +") is alive and updating " + time);
    }
}
    
class Example extends Phaser.Scene
{
    constructor ()
    {
        super();
    }

    preload ()
    {
        this.load.image('undersea', 'assets/pics/undersea.jpg');
        this.load.bitmapFont('atari', 'assets/fonts/bitmap/atari-smooth.png', 'assets/fonts/bitmap/atari-smooth.xml');
    }

    create ()
    {
        this.add.image(400, 300, 'undersea');
        this.add.bitmapText(16, 16, 'atari', "Click for next level InfoPanel", 24);
        this._levelnr = 0;

        this.AddInfoPanel();
        this.input.on('pointerdown', this.AddInfoPanel, this);
    }

    AddInfoPanel()
    {
        this._levelnr += 1;
        var info = new InfoPanel(this);
        info.display("Level " + this._levelnr + " start");
    }
}

const config = {
    type: Phaser.AUTO,
    parent: 'phaser-example',
    width: 800,
    height: 600,
    scene: Example
};

const game = new Phaser.Game(config);

After scene.add.existing() it will be active and visible until removed. In a scene you could check this.sys.displayList.exists(info).

You should call this.destroy() when the second tween completes. That will destroy the Container, Graphics, and Text.

You can remove those references too:

class InfoPanel extends Phaser.GameObjects.Container {
  constructor(scene) {
    // …
    this.once('destroy', this.onDestroy, this);
  }

  onDestroy() {
    this._background = null;
    this._infotext = null;
    this._info_tw = null;
  }
  // …
}

Container already has a this.scene so you can use that instead of creating this._scene, then you won’t have to remove it.

In the scene you would want to avoid or remove any persistent references to the panels, but that looks fine for now because only info is created temporarily in one method.

Thanks for the reply :+1:

But why isn’t the update function of the InfoPanel called? I mean the update() function writes console.log() messages but nothing appears in the javascript console

update() isn’t called automatically because nearly all game objects wouldn’t need it.

You can call it yourself or add the game object to a group with runChildUpdate: true.