Skip to content Skip to sidebar Skip to footer

Best Way For Simple Game-loop In Javascript?

Is there a simple way to make a game loop in JavaScript? something like... onTimerTick() { // update game state }

Solution 1:

There are a varied amount of ways to achieve this using JavaScript depending on your application. A setInterval() or even with a while() statement would do the trick. This will not work for a game loop. JavaScript interpreted by the browser, so it is prone to interrupts. Interrupts will make the play back of your game feel jittery.

The webkitRequestAnimationFrame properties of CSS3 aims to correct this by managing the rendering loop itself. However this is still not the most efficient way to do this and will be prone to jitters if you have alot of objects being updated.

This is a good website to get started with:

http://nokarma.org/2011/02/02/javascript-game-development-the-game-loop/index.html

This site has some good information on the basics of making a game loop. It does not touch upon any sort of object oriented design by any means. The most accurate way to achieve precise timings is by using the date function.

while ((newDate).getTime() > nextGameTick && loops < maxFrameSkip) {
  Game.update();
  nextGameTick += skipTicks;
  loops++;
}

This does not take into account how setTimeout drifts at high frequencies. This will also lead to things getting out of sync and becoming jittery. JavaScript will drift +/- 18ms per second.

var start, tick = 0;
var f = function() {
    if (!start) start = newDate().getTime();
    var now = newDate().getTime();
    if (now < start + tick*1000) {
        setTimeout(f, 0);
    } else {
        tick++;
        var diff = now - start;
        var drift = diff % 1000;
        $('<li>').text(drift + "ms").appendTo('#results');
        setTimeout(f, 990);
    }
};

setTimeout(f, 990);

Now lets put all of this into a working example. We want to inject our game loop into WebKit’s managed rendering loop. This will help smooth out the rendered graphics. We also want to split up the draw and update functions. This will update the objects in our rendering scene before calculating when the next frame should be drawn. The game loop should also skip draw frames if updating takes to long.

Index.html

<html><head><!--load scripts--></head><!-- 
        render canvas into body, alternative you can use div, but disable 
        right click and hide cursor on parent div
    --><bodyoncontextmenu="return false"style="overflow:hidden;cursor:none;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;"><scripttype="text/javascript"charset="utf-8">Game.initialize();
            window.onEachFrame(Game.run);
        </script></body></html>

Game.js

varGame = {};

Game.fps = 60;
Game.maxFrameSkip = 10;
Game.skipTicks = 1000 / Game.fps;

Game.initialize = function() {
    this.entities = [];
    this.viewport = document.body;

    this.input = newInput();

    this.debug = newDebug();
    this.debug.initialize(this.viewport);

    this.screen = newScreen();
    this.screen.initialize(this.viewport);
    this.screen.setWorld(newWorld());
};

Game.update = function(tick) {
    Game.tick = tick;
    this.input.update();
    this.debug.update();
    this.screen.update();
};

Game.draw = function() {
    this.debug.draw();
    this.screen.clear();
    this.screen.draw();
};

Game.pause = function() {
    this.paused = (this.paused) ? false : true;
};

/*
 * Runs the actual loop inside browser
 */Game.run = (function() {
    var loops = 0;
    var nextGameTick = (newDate).getTime();
    var startTime = (newDate).getTime();
    returnfunction() {
        loops = 0;
        while (!Game.paused && (newDate).getTime() > nextGameTick && loops < Game.maxFrameSkip) {
            Game.update(nextGameTick - startTime);
            nextGameTick += Game.skipTicks;
            loops++;
        }
        Game.draw();
    };
})();

(function() {
    var onEachFrame;
    if (window.requestAnimationFrame) {
       onEachFrame = function(cb) {
          var _cb = function() {
                cb();
             requestAnimationFrame(_cb);
          };
          _cb();
       };
    } elseif (window.webkitRequestAnimationFrame) {
       onEachFrame = function(cb) {
          var _cb = function() {
             cb();
             webkitRequestAnimationFrame(_cb);
          };
          _cb();
       };
    } elseif (window.mozRequestAnimationFrame) {
        onEachFrame = function(cb) {
            var _cb = function() {
                cb();
                mozRequestAnimationFrame(_cb);
            };
            _cb();
        };
    } else {
        onEachFrame = function(cb) {
            setInterval(cb, Game.skipTicks);
        };
    }

    window.onEachFrame = onEachFrame;
})();

Even More Information

You can find a full working example, and all of the code here. I have convert this answer into a downloadable javascript framework you can build your games off from.

https://code.google.com/p/twod-js/

Solution 2:

setInterval(onTimerTick, 33); // 33 milliseconds = ~ 30 frames per sec

function onTimerTick() {
    // Do stuff.
}

Solution 3:

requestAnimationFrame is a great alternative available in most browsers now. It does what you're doing with setInterval, but in a baked in way. Probably the nicest thing about it is that it only runs while your tab is focused. That could be a reason not to use it if you want things to run in the background, but often (especially for render loops that only matter when seen) it's great to not be using resources when the tab's not active.

Usage is pretty simple:

function gameLoop(){
  window.requestAnimationFrame(gameLoop);
  Game.update();
}

I hate to post on old threads, but this is a top google result for "javascript game loop" so it really needs to include requestAnimationFrame.

Pollyfill for old browsers copied from paulirish:

(function() {
    var lastTime = 0;
    var vendors = ['ms', 'moz', 'webkit', 'o'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] 
                                   || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = newDate().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());

Deeper explanation and usage tips on creativeJS

Solution 4:

Many of these answers are outdated and suboptimal. The recommended way to do this now is using requestAnimationFrame.

For example:

let previousTime = 0.0;

constloop = time => {
  // Compute the delta-time against the previous timeconst dt = time - previousTime;

  // Update the previous time
  previousTime = time;

  // Update your gameupdate(dt);

  // Render your gamerender();

  // Repeatwindow.requestAnimationFrame(loop);
};

// Launchwindow.requestAnimationFrame(time => {
  previousTime = time;

  window.requestAnimationFrame(loop);
});

Solution 5:

Yep. You want setInterval:

function myMainLoop () {
  // do stuff...
}
setInterval(myMainLoop, 30);

Post a Comment for "Best Way For Simple Game-loop In Javascript?"