0

I'm building a game using the canvas element powered by JavaScript. Part of my player class includes an update() method which is called once per tick. In this method, I'm doing some math to update the players velocity based on keyboard input and also to move the player. Here's a chunk of that code:

// Gradually make the players velocity 0
if(this.xv > 0) {
    this.xv -= 0.1;
} else if(this.xv < 0) {
    this.xv += 0.1;
}
if(this.yv > 0) {
    this.yv -= 0.1;
} else if(this.yv < 0) {
    this.yv += 0.1;
}

// Update player position based on velocity
this.x += this.xv;
this.y += this.yv;

// Update velocity based on keyboard inputs
if(keyState[87]) {
    this.yv -= 0.5;
}
if(keyState[65]) {
    this.xv -= 0.5;
}
if(keyState[83]) {
    this.yv += 0.5;
}
if(keyState[68]) {
    this.xv += 0.5;
}

Now in theory this should all work okay, if the player holds down the W key for 4 ticks their velocity will be 2, and then after 40 more ticks their velocity will be reduced down to 0.

This doesn't work in practice though, as JavaScript doesn't seem to be entirely accurate when it comes to working with floating point numbers. If I console.log() the velocity variables each game tick I get this kind of output:

x: -1.0241807402167069e-14           y: -1.379452108096757e-14
x: 0.09999999999998976           y: 0.09999999999998621
x: -1.0241807402167069e-14           y: -1.379452108096757e-14
x: 0.09999999999998976           y: 0.09999999999998621 

So there's two things that look wrong here, one is that JavaScript never calculates the velocity with good precision, and the other is when the velocity is negative it's always at least -1, which is a problem because the player sprite will now move at 1 pixel per tick.

How can I get more accurate calculations for this task?

James Dawson
  • 5,309
  • 20
  • 72
  • 126
  • Start your research here: [Elegant workaround for JavaScript floating point number problem](http://stackoverflow.com/a/3439981/612202) – dan-lee Feb 19 '13 at 21:03
  • I'm a fan of the "exponential decay" approach to slowing down; I'd replace your first block of `if`s with `this.xv *= 0.9; this.yv *= 0.9;` (or whatever factor "feels" best). You avoid the problem of velocity oscillating around zero after rounding errors, too. – Xavier Holt Feb 19 '13 at 21:06
  • @XavierHolt: that actually mostly fixed the problem. I'm still ending up with numbers like `-0.000021784715049668016` but for my purposes that's fine, I just wanted to avoid the sprite noticeably "creeping". If you add this as an answer I'll accept it. – James Dawson Feb 19 '13 at 21:10

1 Answers1

1

I recommend using an "exponential decay" approach to smoothing out your velocity transitions. Instead of the conditional block you currently use to reduce the velocity to zero, I'd use the following:

this.xv *= 0.9;
this.yv *= 0.9;

Note that this caps your maximum velocity at ten times your constant acceleration, and that changing the rate of decay will change that multiplier. This might be okay, but if it's not, you can tweak the equations to ease into a target velocity:

var p = 0.9;
var q = 1.0 - p;
this.xv = this.xv * p + this.txv * q;
this.yv = this.yv * p + this.tyv * q;

Disclaimer: This has no real basis in Newtonian physics; it's just something I came up with to help smooth out state transitions in a robot. But it worked great for me, and hopefully will for you, too.

Xavier Holt
  • 14,471
  • 4
  • 43
  • 56