Hi all,
I’ve got 2 manager scripts that control a large portion of the game, which are meant to be singletons. As stands, they get initialized once, and then the scene switches from Menu to Match, they get reparented to the new root (with no issues), and then the match ends and the scene switches from Match to Menu, which is where the trouble seems to be.
Quick important code bits (two scripts look identical in this regard, so just going to copy what’s necessary):
GameManager.prototype.initialize = function() {
if (window.GManager !== undefined) {
this.entity.destroy();
}
window.GManager = this;
//additional code
}
GameManager.prototype.ToMainMenu = function() {
let oldRoot = pc.app.root.findByName("Root");
let menuScene = pc.app.scenes.find("Menu");
pc.app.scenes.loadSceneHierarchy(menuScene.url, (err, newRoot) => {
if (err)
throw err;
GClient.entity.reparent(newRoot);
GManager.entity.reparent(newRoot);
oldRoot.destroy();
//additional code
});
}
So the code is identical for entering match and menu, but the key difference is the appearance of the first block of code’s if statement, where it looks to see if GClient/GManager already exists. On
oldRoot.destroy();
I get the following error:
playcanvas-stable.dbg.js:27007 Uncaught TypeError: Cannot read property 'data' of undefined
at ScriptComponentSystem.removeComponent (playcanvas-stable.dbg.js:27007)
at Entity.destroy (playcanvas-stable.dbg.js:43000)
at Entity.destroy (playcanvas-stable.dbg.js:43009)
at gameManager.js?id=31393588&branchId=7541d15a-ca7f-4e76-87f6-b3bcbb9c6757:425
at _loaded (playcanvas-stable.dbg.js:26825)
at Application._preloadScripts (playcanvas-stable.dbg.js:26111)
at playcanvas-stable.dbg.js:26828
at launch.js:10932
which traces down to (Entity.destroy, lines 42999-43013)
for (name in this.c) {
this.c[name].system.removeComponent(this);
}
if (this._parent) {
this._parent.removeChild(this);
}
var children = this._children;
var child = children.shift();
while (child) {
if (child instanceof pc.Entity) {
child.destroy();
}
child._parent = null;
child = children.shift();
}
and finally (removeComponent)
var record = this.store[entity.getGuid()];
var component = entity.c[this.id];
this.fire("beforeremove", entity, component);
delete this.store[entity.getGuid()];
delete entity[this.id];
delete entity.c[this.id];
this.fire("remove", entity, record.data);
If I understand the issue correctly, the removal of the GClient/GManager entity pair is being called when the scene loads, and then oldRoot.destroy() attempts to destroy all children in the scene, including the now destroyed entity pair, which causes an error.
I suppose this is a long way of asking, but is this a bug, or is my method of doing attempting to carry a singleton script between scenes flawed? The idea was to follow what I do in Unity (you’d call it in awake there), but it looks like the issue isn’t in the logic, but a conflict with the PlayCanvas engine itself.