Reputation: 5250
I'm developing a multi-player game using Node.js as a server. At the moment it is simply a 2D triangle the player can drive around the screen. The game loop runs on the server and sends updates of the game world state to the client. The client has a draw loop running which draws the triangle onto the canvas using the updated position and orientation.
The PROBLEM:
I have used setInterval with an interval of 30ms for the draw loop. However, the movement of the game can become fairly jittery. I've tried measuring the time it takes from the start of one loop to the next as shown in the code below. The results show that between loops take anywhere between 1ms to 200ms.
Example console output:
looptime: 59
looptime: 14
looptime: 22
looptime: 148
looptime: 27
The only other things I've got running client side are onkeyup/onkeydown listeners and socket.io listeners to send data to the server. Any help is much appreciated.
Code:
...
function init(){
var canvas = document.getElementById('canvas');
if(canvas.getContext){
setInterval(draw, 30);
}
}
function draw(){
timeNow = new Date().getTime();
console.log('looptime: '+(timeNow-startDrawTime));
startDrawTime = new Date().getTime();
var ctx = document.getElementById('canvas').getContext('2d');
triX = tri.pos.x;
triY = tri.pos.y;
ctx.save();
ctx.clearRect(0,0,400,400);// clear canvas
ctx.save();
ctx.translate(triX, triY);
ctx.rotate(tri.orientation);
ctx.save();
ctx.beginPath();
ctx.arc(0,0,5,0,Math.PI*2, true);
ctx.stroke();
ctx.restore();
//draw tri
ctx.save();
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.moveTo(0, -5);
ctx.lineTo(5, 5);
ctx.lineTo(-5, 5);
ctx.closePath();
ctx.fill();
ctx.restore();
ctx.restore();
ctx.restore();
//console.log('looptime: '+((new Date().getTime())-time));
}
Upvotes: 1
Views: 4030
Reputation: 63812
That's how setTimeout is by design. Javascript runs sequentially, and if it takes longer to complete a loop, well, then it takes longer to complete the loop.
Getting it consistent is merely a matter of performance in this case, and your code has a lot of cruft as-is. I annotated it with some performance improvements.
The reason it runs slow in Firefox is because of the console.log
statement. Take that out and it will run a lot faster. console.log used to be slow in chrome, too, and I only have the dev version, but I think its long since been fixed. Any debugger running will be slower, that's normal.
function init() {
// Never ever reference the DOM unless you absolutely have to
// So we do it once, here, outside of the loop
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
if (canvas.getContext) {
setInterval(draw, 30, canvas, ctx);
}
}
function draw(canvas, ctx) {
timeNow = new Date().getTime();
// console statements can be slow in some browsers, so make sure they're gone from the final product
console.log('looptime: ' + (timeNow - startDrawTime));
startDrawTime = new Date().getTime();
triX = tri.pos.x;
triY = tri.pos.y;
// This clears the transformation matrix and resets the canvas
canvas.width = canvas.width;
ctx.save();
ctx.translate(triX, triY);
ctx.rotate(tri.orientation);
ctx.beginPath();
ctx.arc(0, 0, 5, 0, Math.PI * 2, true);
ctx.stroke();
ctx.restore();
//draw tri
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.moveTo(0, -5);
ctx.lineTo(5, 5);
ctx.lineTo(-5, 5);
// no need to close the path since we aren't stroking
ctx.fill();
//console.log('looptime: '+((new Date().getTime())-time));
}
Upvotes: 2
Reputation: 35309
Ok so theres a few things you can do to speed up performance. The first is cache your context. Put it in a variable outside of the draw function,
var ctx = document.getElementById('canvas').getContext('2d');
Then just reference ctx
in the draw function. Otherwise you are searching the dom EVERY loop which can be expensive.
The next thing I recommend is using requestAnimationFrame
over setTimeout
// requestAnim shim layer by Paul Irish
window.requestAnimFrame = (function(){
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function */ callback, /* DOMElement */ element){
window.setTimeout(callback, 1000 / 60);
};
})();
Basically your browser controls when it needs to be repainted, giving you much smoother animations/movement. The above shim falls back to setTimeout
where not supported, but since you are making a canvas
game it should be supported regardless in the browsers you're using.
You would implement it like this
draw(){
// All your other code etc..
requestAnimFrame( function(){draw()} );
}
Upvotes: 1