[web] JavaScript game loop

Started by
16 comments, last by ucm 12 years, 4 months ago
Of course, this is all assuming that the timing method is any good. Who knows how if each browser's implementation of the Date object is at all accurate.

[Formerly "capn_midnight". See some of my projects. Find me on twitter tumblr G+ Github.]

Advertisement
Quote:Original post by capn_midnight
Of course, this is all assuming that the timing method is any good. Who knows how if each browser's implementation of the Date object is at all accurate.

Usually browsers just take the timestamp from the operating system, so if anything goes wrong, it's usually the operating system's fault. Of course, then there's embedded systems where the browser is part of the firmware itself, but in those cases either the hardware is too weak to do anything useful or it has a decent implementation.

Bear into mind that the worst that could happen is that you'll get a lower real framerate, but the logic framerate will still remain the same no matter what you try to do (well, frameskipping capping aside). The only way such a thing could break is if the browser was completely unable to retrieve the proper time of the day in the first place.
Don't pay much attention to "the hedgehog" in my nick, it's just because "Sik" was already taken =/ By the way, Sik is pronounced like seek, not like sick.
I'm really impressed with Sik's algorithm, it looks like perfect solution to this problem. If anyone wants to see a demo of it implemented in a game engine, I wrote a simple code to test it out. You can find it here. This isn't a game at all, but it shows the frame rate control at work. Feel free to look through the source code if you're curious. In Firefox on my box the graphics start to lag at around 100 objects in the demo, but the motion and interactions remain smooth and on time. Very nice idea!
www.canvaswave.com
I'm not happy with the performance of setInterval (or its retarded brother, setTimeout) and was wondering if y'all knew of a better way.


No you must either use setTimout or setInterval. I would actually recommend you to use setTimeout because setInterval will stack the function calls if the game logic takes longer time to execute than the interval..
I just wrote a quiet in depth article on gameloops for javascript HTML5 games. Read it all here: http://jsteam.org?p=6
You could use the class in the end of that article. I MIT licensed it.
You want to use 'requestAnimFrame". It's supported on most browsers. I use a shim like this to fallback to setTimeout:

window.requestAnimFrame = (function () {
return window.requestAnimFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback, element) {
window.setTimeout(callback, 1000.0 / 60.0);
};
})();


Here's a blog post about it. EDIT: the sidebars of that blog are awesome
Anthony Umfer
I continue to be amazed that this post consistently ranks #1 for searches of "javascript game loop".

Anyway, typedef struct, thank you for your post. That is very interesting. Unfortunately, on iOS 5's Safari, it shows no performance increase, and in Chrome 15 on Windows XP it actually shows a slower performance, almost to the point of Internet Explorer levels. Maybe in the future this will be better, but for now, setInterval or setTimeout seems to be the fastest way.

[Formerly "capn_midnight". See some of my projects. Find me on twitter tumblr G+ Github.]

I've been spending some time on this, and I think I found a good game loop cycle, which is independent of platform speed...


// borrowed from above....
var requestAnimationFrame =(function () {
return window.requestAnimFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback, element) {
window.setTimeout(callback, 1000.0 /60.0);
};
})();

// get the 'canvas', and the 'oil on canvas'
var c = document.getElementById('c');
var oc = c.getContext('2d');

// the 'synthetic' or 'target' fps
var FPS = (1/60);

// how much REAL time passed between updates?
var accumulated = FPS;

// old time stamp
var old = new Date();

// main update loop
function loop() {

// loop this function again
requestAnimationFrame(loop);

// ge the new time stamp
var newDate = new Date();

// add the time that passed between updates
accumulated += ((newDate - old)/1000);

// assign the new time stamp
old = newDate;

// if the accumulated time is greter than our target fps
while(FPS < accumulated){
accumulated -= FPS;

// use the dt in positioning calculations...
// for example... x+=(speed*FPS);
Update(FPS);
}

// render the scene as fast as possible...
Render();
}

// onload()
requestAnimationFrame(loop);



Without all the annoying remarks...


var requestAnimationFrame =(function () {
return window.requestAnimFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function (callback, element) {
window.setTimeout(callback, 1000.0 /60.0);
};
})();

// get the 'canvas', and the 'oil on canvas'
var c = document.getElementById('c');
var oc = c.getContext('2d');
var FPS = (1/60);
var accumulated = FPS;
var old = new Date();

function loop() {
requestAnimationFrame(loop);
var newDate = new Date();
accumulated += ((newDate - old)/1000);
old = newDate;
while(FPS < accumulated){
accumulated -= FPS;
Update(FPS);
}
Render();
}
requestAnimationFrame(loop);
I guess if you really needed each time step, for physics or other calculations...



function loop() {
requestAnimationFrame(loop);
var newDate = new Date();
accumulated += ((newDate - old)/1000);
old = newDate;
while(FPS < accumulated){
accumulated -= FPS;
Update(FPS);
}
}
Render();
}
If you don't need to time step through each pass, this is an alternative 'loop' function....



function loop() {
requestAnimationFrame(loop);
var newDate = new Date();
accumulated += ((newDate - old)/1000);
old = newDate;
if(FPS < accumulated){
var dt = Math.floor(accumulated/FPS)*FPS;
accumulated -= dt;
Update(dt);
}
Render();
}
EDIT: I am now using this game loop:

// setInterval(GameLoop, 1);

var FPS = 50;
var dt = 1/FPS; // synthetic delta time
var last = Date.now()/1000;
var accrued = 0.0;
var ri = 0.0; // render interpolation

GameLoop:function(){
var now = Date.now()/1000;
accrued += (now-last);
CheckInput();
while(accrued > dt){
Update(dt);
accrued -= dt;
}
ri = (accrued/dt); // calculate render interpolation
Render(ri); // pass it to render moving images
last = now;
}

Let me know if you find any 'issues' with this :) I have been working on it for a couple [s]days[/s] weeks now, and like the results.

You can test out the game loop on my html5 test page...

http://www.html5.bunzaga.com

There is an 800x600 canvas with 1000 'targets' bouncing around the screen.

[s]Here is my updated game loop for html5 canvas. It works well for both 'fast' and 'slow' computers...[/s]

// setInterval(GameLoop, 1);
var old_time = Date.now();
var FPS = 1/60;
function GameLoop(){
var new_time = Date.now();
var dt = ((new_time - old_time)/1000);
while(dt > FPS){
Update(FPS);
dt -= FPS;
}
Update(dt);
Render();
old_time = new_time;
}

This topic is closed to new replies.

Advertisement