Behavior of setInteractive(), on(), this, scope, JS class

Hi there.

In a dungeon crawler, I have some doors Images I click to open.

I have this code in a class Game extends Phaser.Scene :

    objectsEvents(objectsList) {

                // ... Loop over objects to add events on

                // Toggle door event
                object.setInteractive().on('pointerdown', this.toggleDoorWithButton, {
                    target: object,
                    game: this
                });

                // ... other events

    }

    toggleDoorWithButton() {
        // Open the door/target
        // Code is decoupled in order to have other triggers that can toggleDoor() directly
        this.game.toggleDoor(this.target);
    }

Where object is an Image GameObject. The only way I found it to work is to give a context to the listener :

{
  target: object,
  game: this
}

In toggleDoorWithButton() I want to get the object clicked and the Game scene in order to open the door in question (because toggleDoor() is a method of Game). In that function this is by default the object itself, the target, the door, but I lose the link to the Game Scene… that’s why I had to define a more complete object with the listener and add Game to it. It seems redundant at first view.

Is it the right way to do that in JS with Phaser ? In pure JS, we can catch the event and get the currentTarget, what I did not manage to do here (the event is the JS one, not a Phaser one).

Before answering, check the code I also try (that came with Phaser samples) but not in a class :

function objectsEvents(objectsList) {

            // ... Loop

            // Toggle door event
            object.setInteractive().on('pointerdown', toggleDoorWithButton);
}

function toggleDoorWithButton() {
    // "this" is the Game Scene
    toggleDoor(this.input.gameObject);
}

In this case this is the Game Scene and the gameObject clicked seems to be stored in this.input.gameObject and it works fine :ok_hand: but this code placed in a class, this.input.gameObject is undefined even if this = Game.

Any best practice on this would be appreciated :blush: :pray:

Answering to myself, I came to these possibilities :

  • The event being “isolated” and asynchronous, sending a complete context with the event is a good choice, it is designed for that.
  • Perhaps it is a question of code architecture/pattern and there is other practices possible (ie linking the Scene to the objects).

And by the way, I have not explored the possibility to get the Game Scene from the object itself, ie GameObject → parent Scene.

:mag: …searching the docs…

Oh well, this property should do the trick :man_facepalming: :

Phaser.GameObjects.Image.scene
A reference to the Scene to which this Game Object belongs.

With that in mind, I could fallback to the default behavior, something like :

    {
                // Toggle door event
                object.setInteractive().on('pointerdown', this.toggleDoorWithButton)
    }

    toggleDoorWithButton() {
        // this = the object where the listener was attached
        // Get the scene directly from the object...
        this.scene.toggleDoor(this);
    }

I’ll try this and come back here to tell you :grin:

One way is

class GameScene extends Phaser.Scene {
  objectsEvents(objectsList) {
    // …
    object.setInteractive().on(
      "pointerdown",
      function () {
        this.toggleDoorWithButton(object);
      },
      this
    );
  }

  toggleDoor(target) {
    // Open the door/target
  }
}
1 Like

Tanks a lot @samme :blush:

Your syntax is exactly what I need :ok_hand:

As you probably know I found we can even make it smaller in ES6 ES5 and arrow function, where the context by default is this :

        object.setInteractive().on('pointerdown', () => {
            this.toggleDoorWithButton(object);
        });

:tada:

In that way, writing the following code, from the context of the class makes more sense (in this use case) :

    toggleDoorWithButton(door) {
        // "this" is the Scene...
        this.toggleDoor(door);
    }

    toggleDoorWithButton(door) {
        // The same code from the point of view of the Image GameObject (door) would be
        // => probably less elegant !
        this.scene.toggleDoor(door);
    }

I struggle a bit with ES5 and ES6 so thank you for the tip and the update :wink: the context notion is always a bit tricky when coming from, in my case, the PHP world, even if I code in JS since a long time.