1

我正在开发一个使用 Node.js 作为服务器的多人游戏。目前它只是一个 2D 三角形,玩家可以在屏幕上行驶。游戏循环在服务器上运行并将游戏世界状态的更新发送到客户端。客户端有一个正在运行的绘制循环,它使用更新的位置和方向将三角形绘制到画布上。

问题:

我在绘制循环中使用了间隔为 30 毫秒的 setInterval。但是,游戏的动作可能会变得相当紧张。我试过测量从一个循环开始到下一个循环所花费的时间,如下面的代码所示。结果表明,循环之间的时间介于 1ms 到 200ms 之间。

控制台输出示例:

looptime: 59
looptime: 14
looptime: 22
looptime: 148
looptime: 27

我在客户端运行的唯一其他事情是 onkeyup/onkeydown 侦听器和 socket.io 侦听器,用于将数据发送到服务器。任何帮助深表感谢。

代码:

...

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));
}
4

2 回答 2

2

这就是 setTimeout 的设计方式。Javascript按顺序运行,如果完成一个循环需要更长的时间,那么完成循环需要更长的时间。

在这种情况下,使其保持一致只是性能问题,并且您的代码原样有很多杂乱无章的东西。我用一些性能改进对其进行了注释。

它在 Firefox 中运行缓慢的原因是由于该console.log声明。把它拿出来,它会运行得更快。console.log 曾经在 chrome 中也很慢,而且我只有 dev 版本,但我认为它早就被修复了。任何调试器运行都会变慢,这是正常的。

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));
}​
于 2012-06-30T05:50:34.213 回答
1

好的,您可以采取一些措施来提高性能。第一个是缓存你的上下文。将它放在绘图函数之外的变量中,

var ctx = document.getElementById('canvas').getContext('2d');

然后只需ctx在绘图函数中引用。否则,您正在搜索可能很昂贵的 dom EVERY 循环。

我推荐的下一件事是使用requestAnimationFrameoversetTimeout

// 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);
              };
    })();

MDN 来源

基本上,您的浏览器控制何时需要重新绘制,为您提供更流畅的动画/运动。上面的 shim 回退到setTimeout不支持的地方,但是由于您正在制作canvas游戏,因此无论您使用的浏览器如何,都应该支持它。

你会像这样实现它

draw(){
    // All your other code etc..
    requestAnimFrame( function(){draw()} );
}
于 2012-06-29T21:04:25.080 回答