Cannot read applyimpuse after scene change

I have a script attached to the root of a scene [CODE BELOW]. If I load directly into the scene everything performs as it should. When I transition from another scene to the scene with code I get the following error.

Cannot read properties of undefined (reading ‘applyImpulse’)

This error actually references to another script within the scene that has a rigidbody. That code below this one.

I believe this error is due to the complications with how scenes are loaded…or something like this??

When I turn off the first script, attached to the root, the error goes away. I think this is because the script attached the root is trying to initialize the systems.rigidbody but it isn’t loaded prior to the applyimpulse step in the code attached to the rigidbody.

So I believe my question is, how can I get the script in the root loaded prior to the code attached to the rigidbody?

Code attached to the scene root.

class FixedUpdate extends pc.ScriptType {

    initialize() {
        const self = this,
            app = self.app,
            fixedStep = 1 / 60,
            systems = app.systems,
            rigidbody = systems.rigidbody;

        const fixedUpdate = (dt) => {
            app.fire('fixed-update', dt);
        };

        let time = 0;
        
        rigidbody.fixedTimeStep = fixedStep;

        systems.off('update', rigidbody.onUpdate, rigidbody);
        app.on('fixed-update', rigidbody.onUpdate, rigidbody);

        self.onUpdate = (dt) => {            
            let newDelta = dt;
            newDelta = pc.math.clamp(newDelta, 0, app.maxDeltaTime);
            newDelta *= app.timeScale;
            time += newDelta;

            while (time >= fixedStep) {
                fixedUpdate(fixedStep);
                time -= fixedStep;
            }
        };
    }

    update(dt) {
        this.onUpdate(dt);
    }

}

pc.registerScript(FixedUpdate, 'fixedUpdate');

Code attached to rigid body in scene with above code attached to root

class FixedUpdateControl extends pc.ScriptType {
//Attach this script to the entity you want to control


    initialize() {       
    
        const self = this;
        this.force = new pc.Vec3();
        this.speed = 0.1;
        var time = 0;
        var rtime = 0;
        

    const onFixed = (dt) => {
        var forceX = 0;
        var forceZ = 0;
    
         if (this.app.keyboard.isPressed(pc.KEY_LEFT)) {
        forceX = -this.speed;
        forceZ += this.speed;
        
            } else if  (this.app.keyboard.isPressed(pc.KEY_RIGHT)) {
        
        forceX += this.speed;
        forceZ = -this.speed;
        
            }
    
    this.force.x = forceX;
    this.force.z = forceZ;

    // if we have some non-zero force
    if (this.force.length()) {

        // calculate force vector
        var rX = Math.cos(-Math.PI * 0.25);
        var rY = Math.sin(-Math.PI * 0.25);
        this.force.set(this.force.x * rX - this.force.z * rY, 0, this.force.z * rX + this.force.x * rY);
        // clamp force to the speed
        if (this.force.length() > this.speed) {
            this.force.normalize().scale(this.speed);
        }
    }

        // Apply impluse
  //THIS IS WHERE THE ERROR IS FLAGGED
            this.entity.rigidbody.applyImpulse(this.force);

        };

        this.app.on('fixed-update', onFixed, self);
    }

}

pc.registerScript(FixedUpdateControl, 'fixedUpdateControl');

You’ve forgotten to clean up the event listener for ‘fixed-update’ when the entity or script is destroyed. When you change scenes, the entities are destroyed but you still have this onFixedUpdate function attached to the app event.

1 Like

Thank you @yaustar !

I have a button that changes the scene that has the fixed-updated script on the root. I modified the code to that button accordingly and it seems to work great.

SceneChangeMM.prototype.onRelease = function(event){
 
    this.app.fire('button:clicked:'+this.entity.name);
    this.app.off('fixed-update');
    this.loadScene(this.sceneID,function(){
        
    });

};

That will remove all event listeners on the fixed-update event at that time whixh may or may not be what you want.

I was thinking more of this where you would remove the a specifc event listener https://playcanvas.com/editor/code/438243?tabs=5665660 (mouse events in this case)

This works for me but I appreciate how to be more specific.

Here’s the example from the project I linked to, stripped down a bit:

MouseInput.prototype.initialize = function () {
    // Add event listener for the mouse down event where 
    // this.onMouseDown is function
    this.app.mouse.on(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);

    // Remove the listeners so if this entity is destroyed
    this.on('destroy', function () {
        this.app.mouse.off(pc.EVENT_MOUSEDOWN, this.onMouseDown, this);
    });
};
1 Like