Phaser: create method of a Scene class calls multiple times

Created on 4 May 2020  路  7Comments  路  Source: photonstorm/phaser

Version

Version: v3.20.1-FB

  • Operating system: macOS Catalina 10.15.4
  • Browser: Google Chrome 81.0.4044.129 (but I don't think that's browser specific)

Description

The create() method of a class that extended from Phaser.Scene calls multiple times instead once.

Example Test Code

I use the phaser3-project-template from @photonstorm.

Override the src/index.js's content with code below:

import Phaser from "phaser";

class SceneOne extends Phaser.Scene
{
    constructor ()
    {
        super({ key: 'SceneOne' });
    }

    create ()
    {
        console.log('Look at me');

        setTimeout(() => {
            this.scene.launch('SceneTwo');
            this.scene.sleep();
        }, 2000);

        this.events.on(Phaser.Scenes.Events.WAKE, () => this.scene.restart());
    }
}


class SceneTwo extends Phaser.Scene
{
    constructor ()
    {
        super({ key: 'SceneTwo' });
    }

    create ()
    {
        this.add
            .text(this.game.config.width / 2, this.game.config.height / 2, 'Tap to back', { fontSize: '48px', color: '#FFFFFF' })
            .setOrigin(0.5, 0.5)
            .setInteractive()
            .on('pointerdown', () => {
                this.scene.wake('SceneOne');
                this.scene.stop();
            });
    }
}

const game = new Phaser.Game({
  type: Phaser.AUTO,
  parent: "phaser-example",
  width: 800,
  height: 600,
  scene: [SceneOne, SceneTwo],
});

Run the game and open the browser's console...

As you see there are two scenes and game will start with the SceneOne then after 2 seconds the SceneOne will launch SceneTwo and itself goes to sleep. On the SceneTwo you should tap (click) the text to go back to the SceneOne. When you tap the SceneTwo will wake SceneOne and itself goes to stop.

Everything works fine before third wake of the SceneOne. After the second wake the Scenes are beginning calls the create() method N+1 times. You can watch this behavior with browser's console.

So our steps to reproduce the bug are:
_Game is running and console is open._

SceneOne and console output:
Look at me

After 2 seconds SceneTwo and you're tapping the text.
SceneOne and Console output:
Look at me

After 2 seconds SceneTwo and you're tapping the text.
SceneOne and Console output:
Look at me
Look at me

After 2 seconds SceneTwo and you're tapping the text.
SceneOne and Console output:
Look at me
Look at me
Look at me

After 2 seconds SceneTwo and you're tapping the text.
SceneOne and Console output:
Look at me
Look at me
Look at me
Look at me

and so on...

How to fix this multiple times calling the create method?

All 7 comments

Change to

this.events.once(Phaser.Scenes.Events.WAKE, () => this.scene.restart());

@samme thanks, you solved my problem!

But I'm not sure to close the issue, because those events behaviors are strange to me.

I would close, since it's not a bug.

Stopping a scene doesn't remove event handlers.

You can see if you add to your create():

console.log('wake', this.events.listenerCount('wake'));

Each restart is adding an additional 'wake' handler, and the next 'wake' event is triggering all of them, and each one restarts the scene.

Yup, agreed, it's up to you to remove any listeners you create.

@photonstorm - I'm running into a similar problem where restarting the scene causes any input handlers to get registered twice. It feels odd to have to do all my cleanup as part of shutdown instead of destroy. Is the expectation that the scene should listen for the SHUTDOWN event and handle disconnecting all it's event handlers there?

All our scenes now how a shared handler that calls this.input.keyboard.removeAllKeys(). Is there any reason that it wouldn't make sense to have this handled automatically in shutdown?

Alternatively, the documentation for Shutdown says:

A Scene that has been shutdown will not run its update loop or render, but it does not destroy any of its plugins or references. It is put into hibernation for later use. If you don't ever plan to use this Scene again, then it should be destroyed instead to free-up resources.

This makes it seem like the plugins are staying around, so I shouldn't disconnect everything. Is there a better way to handle this sort of "one-time" initialization than in the create method to make this easier to handle? The relevant example only attaches a one-time event handler so it doesn't experience this problem or show a standard way of solving it.

Digging into this a little bit more, it appears that the shutdown function in KeyboardPlugin calls this.resetKeys() which resets the state of the key (but does not call removeAllListeners()), and then it calls removeAllListeners on itself.

So I think that keyboard events created using this.input.keyboard.on(...) will be disconnected during Shutdown, but not keys created by this.input.keyboard.addKey(...).

Does that seem like a correct assessment of the situation? If so, I can create a new issue to track this (or we can reactivate this one) and I can pull together a PR to fix it.

@veleek probably create a new issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

BigZaphod picture BigZaphod  路  4Comments

halilcakar picture halilcakar  路  4Comments

samme picture samme  路  3Comments

Colbydude picture Colbydude  路  4Comments

hugoruscitti picture hugoruscitti  路  3Comments