Some basic info on JS and PlayCanvas

I’m just trying to understand both JavaScript and Playcanvas engine.

So I made this code and was wondering why the ‘myTimer’ set interval timer doesn’t work. Specifically it says that ‘translateLocal’ is undefined when I’m trying to use my ‘MoveFunction’. I have an idea why this is happening but wanted to be sure. I know there is a solution outside of this by using ‘this.MoveFunction’ in update and initialize. But wanted more clarification to why this is.

Here is the code:

var MakingScripts = pc.createScript('makingScripts');

MakingScripts.prototype.initialize = function() {
    this.upMove = 0;
    this.isMyTimer = setInterval(myTimer,500);
};

MakingScripts.prototype.update = function(dt) {
    
    //this.entity.translateLocal(0,0.005,0);
    //this.MoveFunction(0.005); 
    
};

MakingScripts.prototype.MoveFunction = function(move){
    this.entity.translateLocal(0,move,0);
};


function myTimer(){
    MakingScripts.prototype.MoveFunction(0.005);
}

myTimer doesn’t exist within the scope of initialize, it only exists after this function has been declared, in ‘window’ scope (I think - I never trust the ‘function funcName()’ pattern - prefer always be explicit: var funcName = function(){}, or window.funcName = function(){} if I want global scope)

Does it work if you use window.myTimer? Or this in combination with var myTimer = function()…? I bet it’d work if you put myTimer func declaration ABOVE the initialise function - I’m not sure what pc does internally with closures, but it’s wholly possible that in your example, myTimer exists trapped in the aether, inaccessible to initialise making an implicitly scoped reference.

1 Like

In myTimer function, you are calling the function prototype which means it doesn’t have a this reference to the script instance.

The way to do this correctly is to have a reference to the script instance when you set up the callback. You can do this in two ways, through bind or using a closure.

Bind method:

MakingScripts.prototype.initialize = function() {   
    setInterval(this.timeCallback.bind(this),500);
};

MakingScripts.prototype.timeCallback = function () {
    console.log("something " + this.entity.name);
};

Closure method:

MakingScripts.prototype.initialize = function() {
    var instance = this;
    
    setInterval(
        function() {
            console.log("something " + instance.entity.name);
        }, 
        500);
};
2 Likes

This was very helpful!! Thank you so much!

Here is my latest test to better understand this :slight_smile: Works exactly as I was expecting. Could you elaborate with me what bind does explicitly? I have a guess that it… well binds it to the object and takes on many of it’s characteristics including translate functions. But I am not sure.

var MakingScripts = pc.createScript('makingScripts');

MakingScripts.prototype.initialize = function() {
    this.move = 0.5;
    this.string = "Default";
    setInterval(this.myTimer.bind(this), 1000);
    
    //Trying to set it's values before hand. 
    this.myTimer(2,"Words");
};

MakingScripts.prototype.myTimer = function(move, string){
    
    if(string === undefined){
        
    } else {
        this.move = move;
        this.string = string;
    }
    
    this.entity.translateLocal(0,this.move,0);
    console.log(this.string);
    
};

In a nutshell, it ‘binds’ the this reference of function when it is called to what value you pass it (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_objects/Function/bind)

If you are using params like you are above, you can add those params to the bind function:

// initialize code called once per entity
KeyboardInput.prototype.initialize = function() {   
    setInterval(this.onInterval.bind(this, 0.4, "hello world"), 500);
};

KeyboardInput.prototype.onInterval = function(move, string) {
    console.log("something " + move + this.entity.name + string);
};
1 Like