Description
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.