Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save godwhoa/e6225ae99853aac1f633 to your computer and use it in GitHub Desktop.
Save godwhoa/e6225ae99853aac1f633 to your computer and use it in GitHub Desktop.
JS Game Loop: Fixed time-step, variable rendering
<canvas id="game-canvas" width="480" height="480">
Please use a browser that supports HTML5 Canvas.
</canvas>

JS Game Loop: Fixed time-step, variable rendering

A fixed time-step with variable rendering using interpolation from the sprites previous position plus its current velocity to calculate its final render position.

Resources: http://gafferongames.com/game-physics/fix-your-timestep/ http://gameprogrammingpatterns.com/game-loop.html http://www.html5gamedevs.com/topic/8716-game-loop-fixed-timestep-variable-rendering/

Forked from Anthony Del Ciotto's Pen JS Game Loop: Fixed time-step, variable rendering.

A Pen by Joseph Daniel on CodePen.

License.

/**
* A polyfill to get the current timestamp.
* window.performance.now will fallback to using
* Date.now or new Date().getTime() on unsupported browsers.
* Author: Anthony Del Ciotto: http://anthonydel.com
*/
(function(window) {
"use strict";
if (typeof window.performance.now === "undefined") {
window.performance.now = (typeof Date.now === "undefined") ?
new Date().getTime() : Date.now();
}
})( window );
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller. fixes from Paul Irish and Tino Zijdel
// MIT license
(function(window) {
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 = new Date().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);
};
}( window ));
(function(window) {
"use strict";
var canvas = document.getElementById("game-canvas");
var ctx = canvas.getContext("2d");
var canvasWidth = canvas.width;
var canvasHeight = canvas.height;
var fps = 30;
var frameDuration = 1000 / fps;
var lag = 0;
var previous = 0;
var startTime = 0;
/**
* just a simple sprite object for demonstration purposes
*/
var sprite = {
x: canvasWidth / 2,
y: canvasHeight / 2,
displayX: 0,
displayY: 0,
amplitude: 150,
period: 2000,
width: 32,
height: 32,
update: function() {
// get the elapsed time since we started
var time = window.performance.now() - startTime;
// perform an oscillation animation
var centerX = canvasWidth / 2 - this.width / 2;
this.x = this.amplitude * Math.sin(time * 2 * Math.PI / this.period) + centerX;
},
interpolate: function(lagOffset) {
// use the lagOffset and the sprites previous x/y pos to interpolate
// the position
this.displayX = (typeof this.previousX !== "undefined") ?
(this.x - this.previousX) * lagOffset + this.previousX : this.x;
this.displayY = (typeof this.previousY !== "undefined") ?
(this.y - this.previousY) * lagOffset + this.previousY : this.y;
},
display: function(lagOffset) {
this.interpolate(lagOffset);
// draw the sprite bro
ctx.fillStyle = '#ffa700';
ctx.translate(this.displayX + (this.width / 2), this.displayY + (this.height / 2))
ctx.fillRect(-this.width / 2, -this.height / 2, this.width, this.height);
}
};
var update = function() {
sprite.update();
};
var display = function(lagOffset) {
ctx.fillStyle = '#FFFFFF';
ctx.fillRect(0, 0, canvasWidth, canvasHeight);
ctx.save();
sprite.display(lagOffset);
ctx.restore();
};
var gameLoop = function() {
requestAnimationFrame(gameLoop);
// calculate the delta or elapsed time since the last frame
var now = window.performance.now();
var delta = now - previous;
// correct any unexpected huge gaps in the delta time
if (delta > 1000) {
delta = frameDuration;
}
// accumulate the lag counter
lag += delta;
// perform an update if the lag counter exceeds or is equal to
// the frame duration.
// this means we are updating at a Fixed time-step.
if (lag >= frameDuration) {
// capture sprites previous frame position
sprite.previousX = sprite.x;
sprite.previousY = sprite.y;
// update the game logic
update();
// reduce the lag counter by the frame duration
lag -= frameDuration;
}
// calculate the lag offset, this tells us how far we are
// into the next frame
var lagOffset = lag / frameDuration;
// display the sprites passing in the lagOffset to interpolate the
// sprites positions
display(lagOffset);
// set the current time to be used as the previous
// for the next frame
previous = now;
};
startTime = window.performance.now();
gameLoop();
})( window );
body {
background-color: #EEEEEE;
}
#game-canvas {
border: 5px solid #d62d20;
border-radius: 4px;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
margin: auto;
position: absolute;
top: 0; left: 0; bottom: 0; right: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment