Sprite still updating after this.destroy()

After I destroy a sprite, I’m still able to console.log(this) it. From what I’ve read, apparently if you want to destroy a game object you have to update all references to the game object to null, so I’m not sure if logging it counts as holding onto a reference to the destroyed object, but I would’ve guessed that trying to log it, would print undefined or nothing at all seeing as it’s been destroyed. So if someone can please advise me on this, I’d greatly appreciate it.

Here’s my relevant code, to provide context.

export default class GameScene extends Phaser.Scene {
  constructor () { super('Game') }

  create() {
    this.timer = this.time.addEvent({
      delay: 1500,
      loop: true,
      callback: () => {
        new Note(this, 60, 150);
      },
    });
  }
}

export default class Note extends Phaser.GameObjects.Sprite {
  constructor(scene, x, y) {
    super(scene, x, y, 'sharp-a');

    this.scene = scene;
    this.scene.add.existing(this);
    this.scene.events.on('update', (time, delta) => { this.update(time, delta)} );

    this.name = 'sharp-a';
    this.debugText = this.scene.add.text(x, y + 30, '', { fontSize: 12 });
  }

  update(time, delta) {
    this.x += 2;

    // I wraped this in a if-statement because without it, "this.debugText.text = `x: ${this.x}`;" would throw an error
    if(this.debugText.scene) {
      this.debugText.text = `x: ${this.x}`;
      this.debugText.x = this.x + 2 - (this.debugText.width / 2);
    }

    if(this.x >= 500) {
      console.log('destroying', this.name, this.x);
      this.debugText.destroy(true);
      this.destroy(true);
    }
  }
}

So as you can see, in the Note’s update function, I start by console.log()ing this.name and this.x and then to proceed to destroy this.debugText and this. The first time this if-statement is entered, I do expect it to log this.name and this.x, because the object hasn’t been destroyed yet, but then it immediately gets destroyed, after which it doesn’t make sense to me that those details should still get logged. If anyone has any insights into what’s happening here, and if I should add/change anything to “properly” destroy the game object, or if this is normal/expected behaviour and I can carry on as-is without any negative consequences, please let me know.

Destroying a sprite will not remove any of the event listeners you’ve added from it, so after the call to this.destroy, the scene’s event emitter still has a reference to the sprite for the update event. You need to use this.scene.events.off in order to remove the listener.

With your current code, this won’t work on its own. events.off expects the same function you passed to events.on, but you never store the arrow function that you create. You should change the call to events.on to this.scene.events.on('update', this.update, this);. The method will still receive its arguments and point to the correct this, but you’ll be able to destroy the event listener with this.scene.events.off('update', this.update, this);.

2 Likes

@Telinc1 thanks for the help, that worked perfectly. I never would’ve figured it out on my own. I’d just like to add that I had to add this.scene.events.off(...) before calling this.destroy(), otherwise it would throw the error Uncaught TypeError: Cannot read property 'events' of undefined. This might be obvious to most, but it wasn’t to me, so I’m pointing it out in case it isn’t obvious to someone else that might stumble upon this question.

I’d also like to ask, if doing the above is enough to ensure that all the note objects I create will get destroyed? I.e. can I keep doing what I’m doing now (continuously creating Note objects in a loop) without eventually running out of memory, assuming I don’t change my code from what you see in my original question?

Right, I forgot to mention this. this.destroy() will set the scene (and any other properties which hold objects) to null so that it can be garbage collected if necessary.

If nothing else has a reference to these objects - which should be the case currently - then yes. Still, the safest way to check is to use the memory tool in your browser’s devtools. Both Chrome’s and Firefox’s can tell you how much memory is being used and show you the objects left in the heap.

1 Like