Skip to content

Containers' children not added to updateList #5817

Closed
@prakol16

Description

@prakol16

Version

  • Phaser Version: 3.55.2
  • Operating system: Ubuntu
  • Browser: any

Description

When an object is added to a container, ADDED_TO_SCENE is never called. This is an issue because many objects use these events to manage whether they are in the updateList as well.

For example,

  • Sprite animations use the update list
  • DOM elements that need to know when to hide themselves use the update list

Both of these break when used in a container, except if the object is first added to the scene and then added to the container in the same frame.

Example Test Code


class MainScene extends Phaser.Scene {
  preload() {
    this.load.spritesheet("mummy", mummyURL, {
      frameWidth: 37,
      frameHeight: 45
    });
  }

  create() {
    const mummyAnimation = this.anims.create({
      key: "walk",
      frames: this.anims.generateFrameNumbers("mummy"),
      frameRate: 16
    });

    // V1 (doesn't work)
    this.makeSpriteV1();
    // V2 (doesn't work)
    // this.makeSpriteV2();
    // V3 (does work)
    // this.makeSpriteV3();
  }

  makeSpriteV1() {
    const sprite = this.add.sprite(50, 50, "mummy");
    this.sprite = sprite;
    // Wait until sprite gets properly added to updateList
    // so that it isn't in _pending anymore
    this.time.delayedCall(1, () => this.addSpriteToContainer());
  }

  makeSpriteV2() {
    // The sprite is never added to the display list at all
    const sprite = new Phaser.GameObjects.Sprite(this, 50, 50, "mummy");
    this.sprite = sprite;
    this.addSpriteToContainer();
  }

  makeSpriteV3() {
    // Sprite is added to `updateList._pending`
    const sprite = this.add.sprite(50, 50, "mummy");
    this.sprite = sprite;
    this.addSpriteToContainer();
    // Sprite was added to container, hence removed from displayList
    // so it is in `updateList._destroyed` as well
    // but it is also in pending
    // so a little bit accidentally it does end up in the updateList after all
  }
  
  addSpriteToContainer() {
    const container = this.add.container(100, 100, [this.sprite]);
    this.sprite.play({ key: "walk", repeat: 7 });
  }
}

In the example above (see https://codepen.io/prakol16/pen/YzQXxpg for the codepen), the mummy's animation doesn't run, except in V3. The reason it works in V3 seems to be something of an accident; when the sprite is first added to the scene, it is added to the update list, and hence added to updateList._pending. Then, when it is added to the container, it is added to updateList._destroy. (This is also kind of a bug, because I think a more expected behavior is that updateList.remove first checks if the item is in _pending, and removes it from _pending if so; otherwise it queues it to be destroyed). Luckily, when these queued events are resolved, it seems _destroy is executed first, and so it is "removed," (when it wasn't in _active to begin with) and then added back from _pending.

If we add a 1 ms delay, so that the sprite properly moves to _active, before adding it to the container, or if we simply never added it to _pending at all by not using the factory constructor, the bug shows up.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions