How should I switch scenes so that there are no bugs?

How should I switch scenes so that there are no bugs? Is the way I changed the scene wrong? Every time there will be an error in the console.

var ChangeScene = pc.createScript('changeScene');

ChangeScene.prototype.initialize = function() {
    this.app.on('scene:change', this.loadScene, this);
};

ChangeScene.prototype.loadScene = function (sceneName) {
    // Get a reference to the scene's root object
    var oldHierarchy = this.app.root.findByName ('Root');
    
    // Get the path to the scene
    var scene = this.app.scenes.find(sceneName);
            
    this.app.scenes.loadSceneSettings(scene.url, function(err){});
        
    // Load the scenes entity hierarchy
    this.app.scenes.loadSceneHierarchy(scene.url, function (err, parent) {
        if (!err) {
            oldHierarchy.destroy();
        } else {
            console.error(err);
        }
    });
};

Who will tell me how to change the scene correctly? I am confused, thank you very much!

@chitiantong, could you please send the JS files causing the errors?

Sounds like you have event listeners that haven’t been cleaned up when the entity that script is on has been destroyed.

I am currently doing Unity development, and I can’t understand the logic of PlayCanvas changing the scene

Is it a public project? Can you post a link to it please?

If not, can you post the script for followCarIcon and the hotspot script please?

1 Like

Here’s an example of where event listeners are not being cleaned up after the script is destroyed:

// initialize code called once per entity
HotSpots.prototype.initialize = function() {
    
    this.spots = [];
    var nodes =  this.hotspots.children;
    for(var i=0; i<nodes.length; i++){
        var front = nodes[i].findByPath('Front');
        var back = nodes[i].findByPath('Back');
        //设置每个热点的颜色
        front.model.material.diffuse = this.color;
        front.model.material.update();
        back.model.material.diffuse = this.color;
        back.model.material.update();
        
        this.spots.push(back);
    }

    
    this.ray = new pc.Ray();
    
    this.defaultForwardDirectionList = [];
    
    this.directionToCamera = new pc.Vec3();
    // Register the mouse down and touch start event so we know when the user has clicked
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
    
    if (this.app.touch) {
        this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
    }
    
    this.hitPosition = new pc.Vec3();
    this.shapes = [];
    for(var i=0; i<this.spots.length; i++){
        //碰撞检测球
        this.shapes.push(new pc.BoundingSphere(this.spots[i].getPosition(), this.rayRadius));
        //记录初始化时热点的朝向
        this.defaultForwardDirectionList.push(this.spots[i].up.clone());
    }
    
    //热点动画
    this.opacity = {alpha:1};
    this.scale = new pc.Vec3(this.scaleBegin, this.scaleBegin, this.scaleBegin);
    this.tween1 = this.app.tween(this.opacity).to({alpha:0}, this.duration, pc.Linear).loop(true).yoyo(false).on('update', this.onFadeout, this).start();
    this.tween2 = this.app.tween(this.scale).to(new pc.Vec3(this.scaleEnd, this.scaleEnd, this.scaleEnd), this.duration, pc.Linear).loop(true).yoyo(false).on('update', this.onScaleout, this).start();
            
    this.on("destroy", function () {
        this.tween1.stop();
        this.tween2.stop();
    });
    
};

We have added a couple of event listeners that add subscribers to the app object:

    // Register the mouse down and touch start event so we know when the user has clicked
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
    
    if (this.app.touch) {
        this.app.touch.on(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
    } 

When the script is destroyed, the subscriber is still on the app object so when it gets called, it is now referencing a non existent script object.

What should be do is to remove the listener when the script is destroyed:

    this.on("destroy", function () {
        this.tween1.stop();
        this.tween2.stop();

        this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
    
        if (this.app.touch) {
            this.app.touch.off(pc.EVENT_TOUCHSTART, this.onTouchStart, this);
        } 
    });

thanks!