variable TimeStep oddness

Topics: General
Apr 30, 2013 at 4:12 PM
Edited Apr 30, 2013 at 4:16 PM
Hello, I've been writing a game using typescript to access the HTML5 canvas element, and since I was hoping to run it on several platforms, I've built it using a variable timestep. Now the odd thing is, since I'm multiplying all velocities by the time step worked out that frame, you would think that there wouldn't be a difference between 60 and 30 FPS, aside from some stuttering in 30 for obvious reasons.

The weird the is that jumping is MUCH higher when at 30FPS than 60, which is the opposite of what I would have thought would happen if something had gone wrong with the timestep. Can anyone see any problems in this code which could cause it?

Player class:
        update() {

            //W:87 S:83 A:65 D:68

            this.velocity.y += this.gravity;

            if (SandLib.Input.isKeyJustDown(16)) {
                this.gravity *= -1;
                this.jumpPow *= -1;
            }

            this.velocity.x += this.accel;

            if (this.velocity.x > this.maxVel) {
                this.velocity.x -= this.decel;
            }

            if (this.x > this.cameraMoveZone) {
                SandLib.Engine.currentScene.camera.x = this.x - this.cameraMoveZone;
            }

            //Collision code here, works fine

            //Jump code vvvvvv
            if (SandLib.Input.isKeyJustDown(32) && this.onGround) {
                this.velocity.y -= this.jumpPow;
                this.velocity.x *= this.jumpBoost;
                this.onGround = false;
                Main.jumpSnd.play();
            }

            if (this.y > 800 || this.y < -800) {
                this.die();
            }            
            super.update();
        }
Engine working out timestep:
static update() {
            Engine.timeInterval = (Date.now() - Engine.lastUpdate) / 1000;
            Engine.lastUpdate = Date.now();
            Input.update();
            currentScene.update();            
            draw();
            Engine.debugText["Interval"] = Engine.timeInterval.toString();
        }
EDIT: oh, and in the players super.update() I multiply his velocity x and y by the timestep.
Coordinator
Apr 30, 2013 at 5:29 PM
Since this is more specific to JavaScript/HTML5 rather than to TypeScript, you might have better luck asking on other forums like StackOverflow.
Coordinator
Apr 30, 2013 at 6:22 PM
this.velocity.y += this.gravity;
Your acceleration also has to be scaled by the timestep. At 60 FPS you're applying twice as much gravity per second as at 30 FPS.
Apr 30, 2013 at 6:44 PM
Hmm, but wouldn't multiplying the whole y velocity by the time at the end sorta do that? Either way though, shall give it a try.
Apr 30, 2013 at 7:45 PM
I think what Ryan is trying to say is that ALL motion in the game has to be multiplied by the delta timer, not just velocity, but anything that influences movement.

Pretend that there are 16ms between each loop, i.e. a perfect 60fps rate. Based on your current code and a velocity of 100:
position = 0
velocity = 100
gravity = 20

... loop

position += gravity
position += velocity * delta (0.016)

... repeat x60 (for 60fps)

position = 1295
Now assume 30fps (so 33.33ms between loop)
position = 0
velocity = 100
gravity = 20

... loop

position += gravity
position += velocity * delta (0.033)

... repeat x30 (for 30fps)

position = 698
Now look what happens if you multiply gravity by the delta as well:
position = 0
velocity = 100
gravity = 20

... loop

position += gravity * delta
position += velocity * delta (0.016)

... repeat x60 (for 60fps)

position = 115
and 30fps:
position = 0
velocity = 100
gravity = 20

... loop

position += gravity * delta
position += velocity * delta (0.033)

... repeat x30 (for 30fps)

position = 118
The small variance between 30fps and 60fps there is to be expected given CPU rounding, JavaScript, milliseconds, etc.
Apr 30, 2013 at 8:18 PM
Yeah I understand, thing is, I've already done that. I don't directly update the position in any of that code, it's all the velocity, which is multiplied by the timestep in the super.update() before the player moves :/
Coordinator
Apr 30, 2013 at 9:02 PM
Edited Apr 30, 2013 at 9:07 PM
Multiplying the velocity alone by the timestep is not enough. You need to multiply the acceleration by the timestep as well.

Consider these parallel statements:
  • At a velocity of 8 miles per hour, you move 4 miles in 30 minutes, or 2 miles in 15 minutes. The length of time spent moving changes the distance.
  • At an acceleration of 8 miles per hour per hour, you accelerate to 4 miles per hour in 30 minutes, or 2 miles per hour in 15 minutes. The length of time spent accelerating changes the velocity.
Your code's version of physics is that if you drop a ball, the speed of the ball does not depend on how long it's been falling, but rather on how many times you've measured its position so far.
Apr 30, 2013 at 9:13 PM
Aah of course. I understand now, thanks! I'll give that a try when I can.