Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Accessing 'this' variable in function passed to setInterval/setTimeout

I'm creating a simple game with a character that can jump, move right, and move left.

I'm having trouble with the jump function, which uses a setInterval.

Here is the function:

 jumpUp: function (speed) {
        setInterval(function () {
            this.yPos += 10;
            this.playerElement.css("top", '-=' + 10);
            alert("dude, im not moving....so stop trying"); //for some reson, this line works and other dont.
        }, 100);
}

I should add that the code works without the setInterval. I really don't have any idea why it's not working when I add the setInterval.

My questions:

  1. What is stopping this code from running?
  2. Is setInterval a good way to make a character look like it jumping and landing? Or should i use different method?

EDIT 1:

fiddle

like image 338
samy Avatar asked Dec 27 '22 10:12

samy


2 Answers

The problem is your use of this. When the function you pass to setInterval is called, this will be the global object (window in browsers). You need to preserve the this value from when you call setInterval. One way of doing this is to store the value of this into a variable, which will then be closed over by the anonymous function (which is a closure):

 jumpUp: function (speed) {
        var self = this;
        setInterval(function () {
            self.yPos += 10;
            self.playerElement.css("top", '-=' + 10);
        }, 100);
}

EDIT:

To answer your second question, a better approach to animating a sprite (like your character) is to store the character's velocity, and then have a single game loop that will calculate the next position of the sprite based on that information. A very simple example would look like:

// Somewhere else in the code:
function tick() {
    // move player by player.velocity.x and player.velocity.y

    // code to decelerate player gradually, stop player when they hit a wall/floor, etc...
    // A very simple example:
    if (player.velocity.y > 0) {
        player.velocity.y -= 1
    }

    // call next tick() (setTimeout, or preferably requestAnimationFrame)
}

// In the player code:
velocity: {x: 0, y: 0},
jumpUp: function () {
    this.velocity.y -= 10;
},
moveRight: function () {
    this.velocity.x += 10;
}
like image 83
George Avatar answered Dec 29 '22 02:12

George


As darma and danronmoon pointed out, you have a scoping problem with this.

Try the following code:

jumpUp: function (speed) {
    var that = this;

    setInterval(function () {
        that.yPos += 10;
        that.playerElement.css("top", '-=' + 10);
    }, 100);
}

I added a variable, that, to maintain the reference to whatever this is supposed to be.

like image 31
Will Klein Avatar answered Dec 29 '22 00:12

Will Klein