0

So I'm making a javascript game. And I have this basic gameloop engine.

var left = 0,
    speed = documentHeight/300;

var keyState = {};
window.addEventListener('keydown',function(e){
    keyState[e.keyCode || e.which] = true;
},true);    
window.addEventListener('keyup',function(e){
    keyState[e.keyCode || e.which] = false;
},true);

function gameLoop() {
  if (keyState[39]) {
     left -= speed;
     move();
  }
  if (keyState[37]){
     left += speed;
     move();
  }

  function move() {
     element.style.left = left + 'px';
  }

  setTimeout(gameLoop, 20);
}
gameLoop();

I have it all at 100% height so I have speed set relative to the document height. It works just fine on my pc, and it is consistent even when resizing. But when I test it on my iMac, the elements move much slower. I've also tried window.requestAnimationFrame(gameLoop) but same thing happens.

Does this have to do with some screens processing more fps? What would be the best way to achieve a consistent speed on all computers/screens?

Magdi Gamal
  • 681
  • 1
  • 9
  • 25
  • Take a look at requestAnimationFrame eg this: https://stackoverflow.com/questions/19764018/controlling-fps-with-requestanimationframe – keja Nov 29 '17 at 20:45
  • Don't couple physics and framerate. – zero298 Nov 29 '17 at 20:49
  • For the clarity of any future visitors, zero298's comment is nice and succinct way to say what my answer is explaining. – samanime Nov 29 '17 at 21:32

1 Answers1

2

At 20 milliseconds per frame, that's 50 fps. That's not super high, but it can get slowed down on slower computers (because it can't keep up).

A safer approach is to still run it at your desired frame rate, but then check the delta of the time between frames and do movements based on the actual time that elapsed instead of assuming a constant FPS.

The easy way to do that is just record the last time each loop, then subtract from the current time and scale by some factor based on the amount of time, something like:

So I'm making a javascript game. And I have this basic gameloop engine.

var left = 0,
    speed = documentHeight/300,
    lastTime = Date.now();

var keyState = {};
window.addEventListener('keydown',function(e){
    keyState[e.keyCode || e.which] = true;
},true);    
window.addEventListener('keyup',function(e){
    keyState[e.keyCode || e.which] = false;
},true);

function gameLoop() {
  const currentTime = Date.now();
  const delta = currentTime - lastTime;
  lastTime = currentTime;

  if (keyState[39]) {
     left -= speed * delta;
     move();
  }
  if (keyState[37]){
     left += speed * delta;
     move();
  }

  function move() {
     element.style.left = left + 'px';
  }

  setTimeout(gameLoop, 20);
}
gameLoop();

Then, just set the "speed" value relative to something per millisecond (like 5px per millisecond, or .00033% of the screen height per millisecond). By doing that and multiplying by time elapsed, you'll get smoother movement even if your actually loop speed isn't super consistent.

Additional note: sometimes, depending on your application, you may want to keep track of the remainder as well. If you are doing something like "10px per second", but 20ms have passed, that's 10 / 1000 * 20 = 0.2px. Since you can't actually move .2px, you might want to track the remainder and add it back in on the next loop.

Alternatively, you can let your position be a decimal and resolve it in the draw() phase. This can be a cleaner approach, and one I usually take instead.

samanime
  • 25,408
  • 15
  • 90
  • 139
  • Hey, thanks a lot! I got a little lost on the setting speed per ms part. Can you explain that a little more? How exactly do I set it up per millisecond? – Magdi Gamal Nov 29 '17 at 20:59
  • Since your `delta` is going to be "number of milliseconds since last loop", you'll want to scale everything based on that amount. So, if you originally meant for it to move "500px per second", divide that by 1000 (convert seconds to milliseconds) so it's ".5px per millisecond". Then, if you multiple ".5px per millisecond" by "5ms since last loop", you'll move the amount you wanted to move. So set your speed variable to `.5` in that example. That help? – samanime Nov 29 '17 at 21:05
  • Perfect! Thank you so much! Great explanation and the speed is consistent on both my computers now. – Magdi Gamal Nov 29 '17 at 21:27