Clearing old script attributes and unused scripts in new scene

Hi,

I had a look around and found a really old thread on PlayCanvas answers. I’m having some issues that when I click through each scene, some of the attributes for the scripts aren’t loading, and in particular, on one scene, I am getting errors from the script failing (even though it doesn’t appear in my new scene).

Is there a way to force a refresh when I load a new scene so that I dump the old scripts and look for new settings attached to any scripts?

I guess it would be in this section of code but any help would be gratefully received.

    var changeLocation = function(sceneID) {
        this.app.root.destroy();
        this.app.loadSceneHierarchy(sceneID + '.json', function(err, entity) {
            entity.findByName('Root').script.rootController.setupScene();
        });
    }.bind(this);
};

Thanks!

Hi,

I think I found where the issue is, in not destroying the old scene properly. I’ve tried the below and still having issues, even after debugging I can see that scripts are still being referred to that should be killed.

Can anyone help?

Thank you.

var menuItems = document.getElementsByClassName('menu-item');
    var i;
    for (i = 0; i < menuItems.length; i++) {
        menuItems[i].addEventListener("click", function() {
            var sceneID = this.dataset.id;
            changeLocation(sceneID);
        });
    }
    
    var changeLocation = function(sceneID) {
    var oldHierarchy = this.app.root.findByName ('Root');
         oldHierarchy.destroy ();
        this.app.loadSceneHierarchy(sceneID + '.json', function(err, entity) {
            entity.findByName('Root').script.rootController.setupScene();
        });
    }.bind(this);
};

Hi,

Sorry, hope this doesn’t count as spamming but I’m sure others will come up against this issue too!

I read that maybe it wasn’t working as I was calling the wrong root - so I made an entity to contain each level’s content and then am trying to destroy this - but on the next scene I’m still getting errors thrown up from a script in the previous scene!

Could anyone point out where I’m going wrong?

var RenderUi = pc.createScript('renderUi');

RenderUi.attributes.add('logoHtml', {
    type: 'asset',
    Title: 'Logo HTML'
});
RenderUi.attributes.add('menuHtml', {
    type: 'asset',
    Title: 'Menu HTML'
});
RenderUi.attributes.add('uiCss', {
    type: 'asset',
    Title: 'UI CSS'
});

// initialize code called once per entity
RenderUi.prototype.initialize = function() {
    var style = document.createElement('style');
    style.innerHTML = this.uiCss && this.uiCss.resource || '';
    document.head.appendChild(style);
    
    var logoDiv = document.createElement('div');
    logoDiv.innerHTML = this.logoHtml && this.logoHtml.resource || '';
    document.body.appendChild(logoDiv);
    
    var menuDiv = document.createElement('div');
    menuDiv.innerHTML = this.menuHtml && this.menuHtml.resource || '';
    document.body.appendChild(menuDiv);
    
    var menuItems = document.getElementsByClassName('menu-item');
    var i;
    for (i = 0; i < menuItems.length; i++) {
        menuItems[i].addEventListener("click", function() {
            var sceneID = this.dataset.id;
            changeLocation(sceneID);
        });
    }
    
    var changeLocation = function(sceneID) {
    var oldHierarchy = this.app.root.findByName ('Level');
         oldHierarchy.destroy();
        this.app.loadSceneHierarchy(sceneID + '.json', function(err, entity) {
            entity.findByName('Level').rootController.setupScene();
        });
    }.bind(this);
};

The code in your second post ‘should’ work. Difficult to tell without seeing the project as a whole. Is the project public and if so, can you post a link?

I have encountered these issues as well – scripts only present in one scene will continue running and thus throw errors when another scene that does not use these scripts is loaded. I’m leaning now on having to do checks in each script if is is being run in the appropriate scene but this feels like a very backwards way to do it indeed.

Is this at runtime or in the editor?

I’m experiencing this at runtime.

Are you destroying the old scene objects when you load the new scene? Is it possible to share a link to the project?

I’ll try to make time to do a minimal test case but yes I am destroying the objects by using the method found in the scene switching tutorial. Basically:

var oldHierarchy = app.root.findByName('Root');
app.loadScene(scene_path, function () {
   oldHierarchy.destroy();
});

And to clarify – the new scene is loaded correctly but the scripts from the previously loaded scene are throwing errors, they do not seem to be correctly deregistered/destroyed.

Ahhh… Have you registered any event listeners and the such? If so, check if they have been deregistered as well when destroyed.

1 Like

Ah yes, so for example in the basic playcanvas MouseInput initialize method:

        this.on('destroy', function() {
            this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
            this.app.mouse.off(pc.EVENT_MOUSEUP, this.onMouseUp, this);
            this.app.mouse.off(pc.EVENT_MOUSEMOVE, this.onMouseMove, this);
            this.app.mouse.off(pc.EVENT_MOUSEWHEEL, this.onMouseWheel, this);

            window.removeEventListener('mouseout', onMouseOut, false);
        });

It might just be that I’ve missed to deregister some listeners on the destroy event like this – that’s probably it. Not really thinking clear when I assumed changing scene would give a clean slate automatically but I understand now.

Although when returning to a previously loaded scene, the initialize method is not triggered on the scripts in that scene. So what hook would I use to reattach the listeners?

It should as it’s creating new objects. If you can make a minimal reproducible case, I see if I can get some time to look at it. I don’t normally use scenes but I do have some knowledge. It could be that you’ve found a bug :confused:

1 Like

Thank you again for your help. A minimal reproducible case solved it for me. I think my problems boiled down to implementing the (ondocumented) app.loadScene method that is being used in the _start.js that is included in the project download thinking it would load both settings and hierarchy.

In any case: for future reference, switching scene hierarchy and settings work fine: https://playcanvas.com/project/530157/overview/scene-switching

2 Likes

Hi @yaustar,

I’ve a dumb bug and can’t figure out. I’ve a resize event, registered on the window object, still called whereas my object is destroyed. The removeEventListener doesn’t work. I’m missing something obvious but what?

ApplyOutline.prototype.initialize = function() {
  
    window.addEventListener("resize", this.onResize.bind(this));
    this.entity.script.on('destroy:applyOutline', function (scriptInstance)
    {
        console.log("destroy applyOutline"); // called
        window.removeEventListener("resize", this.onResize); // don't work
        window.removeEventListener("resize", this.onResize.bind(this)); // don't work
    }, this);
};

Try the following:

ApplyOutline.prototype.initialize = function() {
    var resizeHandler = this.onResize.bind(this);
    window.addEventListener("resize", resizeHandler);
    this.on('destroy', function () {
        console.log("destroy applyOutline"); 
        window.removeEventListener("resize", resizeHandler); 
    }, this);
};

.bind creates a new function (Function.prototype.bind() - JavaScript | MDN) so you need to store that somewhere so you can unregister it.

2 Likes

Wow, thank you for the answer, it works fine!

I’d forgotten this particularity.