Emit Events Across Scenes

Hi, I’m using ES6 with webpack, node.js, and the Phaser 3 game template , so that I can more easily reuse code elements. My game has all of the typcial Scenes (Boot, Preload, Title, GameLevel, HUD, and Credits). It’s a tile based game, and all of that works fine, my player collects pickups, etc.

My HUD Scene just has a ‘score’ field and a ‘countdown timer’ field. It is launched from my GameLevel Scene, and that works fine as well.

Here is the problem. From a ‘collectPickup’ function located in GameLevel, i want to emit an event, which is received fine as long as the listener is also in the GameLevel. If the listener is in HUD, then it is not received.

Here are snippets of my code:
////////////////////////////////////////////////////////////////////////////
import ‘phaser’;

import Player from ‘…/classes/comps/player.js’;//G is a global const
import Platform from ‘…/classes/comps/platform.js’;
import Controller from ‘…/classes/mc/controller.js’;//controller is a global
import Emitter from ‘…/classes/util/emitter.js’;
import Listener from ‘…/classes/util/listener.js’;

export default class Level_OneScene extends Phaser.Scene {

constructor() {
	super('Level_One');
	this.config = game.config;
    this.scene = this;		
}

init(data) {
    //these are instantiated in the BootScene, and passed into the scene.start

	this.model = data[0];
	this.G = data[1];
    this.controller = new Controller();

    this.emitter = new Emitter();
    this.emitter.addListener('upPoints', this.test, data);
    
}

preload() {
	this.load.image('enemy', 'assets/images/enemy_1.png');
}

create() {
    this.scene.launch('HUD', [this.model, this.G, this.controller, this.emitter]);
    }
collectPickup(player, pickup) {    
    this.starObjectsGroup.remove(pickup, true);
    this.model.score = this.model.score + 1;//console.log(this.model.score);
    // this.emitter.emit(this.G.UP_POINTS, 1);//sent to the controller, only received by this class
    this.emitter.emit('upPoints', 54);
}

};

Now the code for HUD
import ‘phaser’;

import AlignGrid from ‘…/classes/util/alignGrid.js’;
import Align from ‘…/classes/util/align.js’;
import HudBox from ‘…/classes/comps/hudBox.js’;
import Controller from ‘…/classes/mc/controller.js’;
import Emitter from ‘…/classes/util/emitter.js’;
import Listener from ‘…/classes/util/listener.js’;
//model and constants objects are passed by calling scene

export default class HUDScene extends Phaser.Scene
{
constructor() {
super(‘HUD’);
this.config = game.config;
this.scene = this;
}

init(data) {
  //initialize plugins, receive data from other scenes (points for next level, etc)
  this.model = data[0];
  this.G = data[1];//received via scene.add or scene.start. in this case TitleScene
  this.emitter = new Emitter();
  this.emitter.addListener('upPoints', this.test, data);
}

test(date) {
    console.log('hud ' + data);
}

create() {
// this.controller = new Controller();

  /////////////////////////////////////////////////////////////

    this.sb = new HudBox({scene: this, type: 'score', fontFamily: 'Roboto', fontSize: '24px', fill: '#eaf0f9', constantsRef: this.G, modelRef: this.model});
    this.sb.text1.width = this.config.width / 8;
    this.alignGrid.placeAtIndex(1, this.sb);
    this.sb.setScrollFactor(0);

    this.tb = new HudBox({scene: this, type: 'timer', setTime: 5, fontFamily: 'Roboto', fontSize: '24px', fill: '#eaf0f9'});
    this.tb.text1.width = this.config.width / 8;
    this.alignGrid.placeAtIndex(8, this.tb);
    this.tb.setScrollFactor(0);

    this.scene.bringToTop();//brings the HUD to top of z-index?

}

scoreUpdated() {console.log('hudScene -scoreUpdated');
  this.sb.scoreUpdated();
}

}

Once again, this stuff works, except for the Scene to Scene Events. Can anybody point me in the right direction on this?
Thanks in advance

Scenes already have a built-in emitter, in events, so you don’t have to create your own. (You still can if you want, though.)

Make sure you’re emitting from and listening to the same emitter. It should be something like

// In Level_OneScene:
this.events.emit('upPoints' /*…*/);

// In HUDScene:
this.scene.get('Level_One').events.on('upPoints' /*…*/);
2 Likes

thanks samme, that works perfectly. its not so much that i wanted to use a custom eventemitter, just tried that because i couldnt get the phaser built-in to work.
thanks once again

Can you also please explain how to reference event arguments from handler?
For example, if in Scene1 event is emitted with this.events.emit('myEvent', 15), is it correct to handle it in Scene2 with this.events.on('myEvent', function (arg) {/*do something with arg*/})?

The arguments are right but you need to use the same emitter, otherwise they’ll never communicate:

// Scene1
 this.events.emit('myEvent', 15);

// Scene2
this.scene.get('Scene1').events.on('myEvent' function (num) {});

samme, etc

Good code, but brittle architecture,
Why make your HUD responsible for knowing what scene is active or which might be sending signals?
Why not add this to any scene that needs to report to the HUD?

// declaration
this.HUDevent = (e, data)=> this.scene.get('HUD').events.emit( e, data);

// usage
this.HUDevent( "score", points);

2 Likes