BaseScene, HUD and Event Emitter file structure in Phaser 3.60

I am not reinventing the wheel here by any means in laying out the file structure I am using for Mr Football, but I find it helpful to have more examples of this sort of stuff because they can tend to be a bit sparse at times. In particular, Event Emitters can be tricky and I haven’t seen a whole lot of full examples with all three of the aforementioned elements working together.

We start with the BaseScene, a file that everyone should be using in a multi-file Phaser project. theatre is the name I am giving to the Event Emitter, continuing the Phaser scenes metaphor.

import phaser from "phaser";
import { theatre } from './hud.js';

export class BaseScene extends Phaser.Scene
{
constructor (config)
    {
    super(config);
    }

init ()
    {
    // declare variables and constants to be referenced in all regular game scenes here with the prefix this
    // e.g. this.foo = 'bar';
    
    // DO NOT declare listeners to the theatre here with .on, as they will spam in every new scene
    }
    
// no create() needed or desirable in the BaseScene, if you want overlay objects use HUD

nextScene(oldscene, newscene, payload)
    {
    this.scene.stop(oldscene);
    this.scene.run(newscene, payload);
    bigtop.emit('hudFocus', lens);
    }
}

Instead of invoking this.scene.start in your regular game scenes, you will use this.nextScene which is accessible through BaseScene.

import { BaseScene } from './base.js';

const theatre = new Phaser.Events.EventEmitter();
export { theatre };

export class HUD extends BaseScene {
    constructor () {
        super({
            key: 'hud'
        });
    }

init ()
    {
    super.init();
    }

create () {
    for (let fnc of [/* function names to run via bigtop emitter */]) {
        theatre.on(fnc, this[fnc], this); }
    
    // HUD display stuff 
    
    // Example of invoking theatre emitter:
    // foo.on("pointerdown", () => theatre.emit('barEvent', payload) );
    }
    
hudFocus()
    {
    this.scene.run('hud');
    this.scene.bringToTop('hud');
    }
}

I made two key initial mistakes with this structure which I have only just now corrected, thanks to the advice of @Kal_Torak on the Phaser Discord. The key to organising a BaseScene which forms the basis of every regular game scene in conjunction with a HUD that is persistently active and is brought to the top of every newly started (or woken) scene is making sure you don’t double up on declaring listeners and emitters. Originally, I had the emitter declared in the BaseScene, and listeners for separate events in both the baseScene and HUD. Since the baseScene is incorporated into multiple other scenes, this leads to spamming listeners and looped memory leaks.

Finally, there is one line to add to the bottom of the create() in your home scene – the one that the user sees directly after the preloader…

this.scene.run('hud');

… and also one that you add to the bottom of the create() in every scene (including home) which uses BaseScene as its base:

theatre.emit('hudFocus');

This structure allows you to add events for tutorials and other popups in the nextScene function, as well as a “lens” system using arguments to the hudFocus event and the registry to enable manipulation of objects in the HUD scene. I will detail these in subsequent posts.

EDIT: I have re-forked my original BaseScene gist from a few years ago and added the above code. My first gist was buggy, hopefully this one is not as it is re-forked from @samme‘s revision.