Javascript设置游戏平局循环的时间间隔不一致

Javascript设置游戏平局循环的时间间隔不一致,javascript,canvas,rendering,setinterval,Javascript,Canvas,Rendering,Setinterval,我正在开发一个多人游戏,使用Node.js作为服务器。目前,它只是一个2D三角形,玩家可以在屏幕上驾驶。游戏循环在服务器上运行,并向客户端发送游戏世界状态的更新。客户端有一个绘制循环运行,它使用更新的位置和方向将三角形绘制到画布上 问题是: 我使用了间隔为30ms的setInterval进行绘制循环。然而,游戏的运动会变得相当紧张。我试着测量从一个循环开始到下一个循环所花费的时间,如下面的代码所示。结果表明,环路之间的时间介于1ms到200ms之间 控制台输出示例: looptime: 59 l

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

问题是:

我使用了间隔为30ms的setInterval进行绘制循环。然而,游戏的运动会变得相当紧张。我试着测量从一个循环开始到下一个循环所花费的时间,如下面的代码所示。结果表明,环路之间的时间介于1ms到200ms之间

控制台输出示例:

looptime: 59
looptime: 14
looptime: 22
looptime: 148
looptime: 27
我在客户端运行的其他东西只有onkeyup/onkeydown监听器和socket.io监听器,用于向服务器发送数据。非常感谢您的帮助

代码:


好的,那么您可以做一些事情来提高性能。第一个是缓存上下文。将其放入draw函数之外的变量中

var ctx = document.getElementById('canvas').getContext('2d');
然后只需在draw函数中引用
ctx
。否则,您将在每个循环中搜索dom,这可能会很昂贵

接下来我推荐使用
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);
              };
    })();

基本上,浏览器可以控制何时需要重新绘制,从而使动画/运动更加流畅。上面的垫片返回到不受支持的
setTimeout
,但由于您正在制作
canvas
游戏,因此无论您使用何种浏览器,都应该支持该垫片

您将这样实现它

draw(){
    // All your other code etc..
    requestAnimFrame( function(){draw()} );
}

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

在这种情况下,保持一致性仅仅是一个性能问题,您的代码也有很多缺点。我对它进行了一些性能改进

它在Firefox中运行缓慢的原因在于
console.log
语句。把它拿出来,它会跑得更快。在chrome中,console.log的速度也很慢,我只有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));
}​

谢谢我实施了你的建议,它似乎以每16毫秒一帧的速度平稳运行。然而,现在Firefox的情况更糟,它的帧速率更不稳定,从1到220+毫秒。我注意到,当我打开Firebug控制台或chrome开发者控制台时,情况变得更糟。有什么建议吗?认为大量的
save
restore
调用对性能非常有害,实际上这是代码中真正需要的吗?注意,
setInterval(draw(canvas,ctx),30)
将立即调用
draw
,并设置对draw返回值的调用——它不是函数或字符串,因此,如果它甚至能够成功地通过没有一个打字错误,我会非常惊讶。改为说
setInterval(draw,30,canvas,ctx)
。并声明
draw
以获取要传递的参数:哇!这就是为什么在JSFIDLE中有完整的代码很好!否则,我就无法手工提供答案。好眼力!顺便说一句,据我所知,
console.log
在Chrome中更快,因为它延迟了实际的日志记录。(因此会出现问题;例如,如果您
console.log
一个对象,然后立即修改它,您经常会在日志中看到修改过的对象。)在chrome中,我体验到,将文本附加到dom实际上比将其记录到控制台要快。
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));
}​