[SOLVED] Changing Scenes and Events

After building a main Menu Scene and an activity scene, I’m finding myself encountering a lot of undefined errors when switching back and forth between them.

The scenes work independently, or if you visit them just once, but the switch back causes some errors. One error was for my raycast script which I think I solved by just unsubscribing the mouse from the move and down events when the root entity is destroyed, but the strangest one so far is:

TypeError: this.entity.children[0] is undefined

This error only appears after coming back from the activity scene.

Here is a sample of the code that is being called there:

app.on("ui:keyattract4",
     function() {
        this.activekey = self.createkey;
        this.entity.children[0].element.text = self.activekey[Math.floor(Math.random() * self.activekey.length)];
        shrinktween.complete = true;
        growtween.start();
    }, this);

Obviously, I know that the children still exist (I’m looking at them!), which makes me think it might be trying to access the entities of the previous hierarchy?

Any insight to what is generally would be greatly appreciated.

Here is how I am currently handling my scene changes:

var SceneManager = pc.createScript('sceneManager');

SceneManager.attributes.add('startScene', {
    type: 'string',
    title: 'Start Scene'
});

// initialize code called once per entity
SceneManager.prototype.initialize = function() {
    
    var app = this.app;
    var self = this;
    
    this.manager = this.entity;
    
    this.currentScene = null;

    app.on('initStart', function() {this.initStart(this.manager);}, this);
    
    app.on('changeScene', this.changeScene, this);
    
};

SceneManager.prototype.changeScene = function(targetscene) {
    
    var oldHierarchy = this.app.root.findByName('Root');
    var self = this;
    
    oldHierarchy.removeChild(this.manager);
    
    this.loadStart(targetscene, function(newHierarchy) {
        newHierarchy.addChild(self.manager);
        
        oldHierarchy.destroy();
        
        
    });
    

};

SceneManager.prototype.loadScene = function(name, callback) {
            //path to the scene
    var sceneRegObj = this.app.scenes.find(name);
    
    
    //load the scenes entity hierarchy
    this.app.scenes.loadSceneHierarchy(sceneRegObj.url, function(err, parent){
        if(!err){
            this.currentScene = sceneRegObj;
            callback(parent);
        } else {
            console.error (err);
        }
    });
};

SceneManager.prototype.initStart = function(manager) {
    var self = this;
    var oldHierarchy = this.app.root.findByName('Root');
    
    oldHierarchy.removeChild(manager);
    
    this.loadStart(this.startScene, function(newHierarchy) {
        newHierarchy.addChild(manager);
        
        oldHierarchy.destroy();
        
        
        
        
    });
    
};

SceneManager.prototype.loadStart = function(name, callback) {
        //path to the scene
    var sceneRegObj = this.app.scenes.find(name);
    
    
    //load the scenes entity hierarchy
    this.app.scenes.loadSceneHierarchy(sceneRegObj.url, function(err, parent){
        if(!err){
            this.currentScene = sceneRegObj;
            callback(parent);
        } else {
            console.error (err);
        }
    });
};

// SceneManager.prototype.postInitialize = function() {

// };

// swap method called for script hot-reloading
// inherit your script state here
// SceneManager.prototype.swap = function(old) { };

// to learn more about script anatomy, please read:
// http://developer.playcanvas.com/en/user-manual/scripting/

When changing scenes and the entity destroyed, was this event unsubscribed? ie app.off("ui:keyattract4", /*etc*/).

You will need to do the same to any event that is listen to a ‘persistent’ object that exists between changing scenes. e.g mouse, app, touch, etc

Thank you @yaustar! I had tried that before, but it broke the event even on start up. That gave me the idea to instead of finding Root by name to set an attribute in the script containing the Root entity and setting it in the editor. Is that the preferred way to do it?

It worked with the example above.

I don’t know the exact context when you register the listener. Assuming it is on the initialize call, I would do the following:

var app = this.app;
var onUiAttract4 = function() {
   // Some logic
}.bind(this);

app.on("ui:keyattract4", onUiAttract4, this);

this.on('destroy', function() {
    app.off("ui:keyattract4", onUiAttract4, this);
}, this);

So when the script is destroy, it removes the listener.

I think you just accidentally identified where I went wrong weeks ago! I had been trying

this.entity.on('destroy', /*etc*/);

on different sub-objects and it never worked. It had never occurred to me to do just this.on, but it works!

Thank you for the insight!

Hmm… this.entity.on should have work as well unless you were only removing the script :thinking: