HTML5 / JS - 波涛汹涌的游戏循环

时间:2012-12-10 17:26:08

标签: javascript html5 canvas game-loop

我一直在努力用HTML5 / JS制作一个简单的游戏。我已经设法为状态机和游戏循环准备好基本的骨架,但即使制作一个简单的矩形移动也会产生一些“有形”的不稳定性。

更重要的是,在将它闲置一段时间后(跳出来),它看起来非常快,使得一个简单的按键移动矩形远远超过它应该。

我正在使用setTimeout进行游戏更新。在每个'tick'期间,我调用当前状态的更新函数,即

State.prototype.update = function(ms) {
    this.ticks += ms;

    var updates = 0;
    while(this.ticks >= State.DELTA_TIME && updates < State.MAX_UPDATES) {
          this.updateState();

          this.updateFrameTicks += State.DELTA_TIME;
          this.updateFrames++;

          if(this.updateFrameTicks >= 1000) {
              this.ups = this.updateFrames;
              this.updateFrames = 0;
              this.updateFrameTicks -= 1000;
          }

        this.ticks -= State.DELTA_TIME;
        updates++;
    }   

    if(updates > 0) {
          this.renderFrameTicks += updates*State.DELTA_TIME;
          this.renderFrames++;

          if(this.renderFrameTicks >= 1000) {
              this.rps = this.renderFrames;
              this.renderFrames = 0;
              this.renderFrameTicks -= 1000;
          }

          this.renderState(updates*State.DELTA_TIME);
    }

};

我们的想法是使用Game.update尽可能频繁地呼叫setTimeout,然后将经过的时间传递给State.update。仅当状态累积的时间大于或等于固定更新时间步长时,State.update才会更新状态。如果状态实际更新,State.update也将呈现/重绘当前状态,确保状态显示与状态模拟匹配。

现在,我知道requestAnimationFramesetTimeout效果更好,但理论上当前版本应该有效,除非我犯了一个根本性的错误。

这是我到目前为止:http://jsbin.com/ogicec/1Edit

你可以清楚地看到它有点不稳定,如果你长时间退出并重新进入,它似乎比正常情况“运行得更快”。

我无法确定问题所在,所以非常感谢帮助!

修改 我将更新分离并渲染状态的部分,使用setTimeout进行更新,使用requestAnimationFrame进行渲染。它解决了问题,整个事情感觉更加一致。但是,与此同时,性能仍然不稳定,我希望确保它足够顺畅,然后才能添加更多与游戏相关的代码。

以下是更新的JSBin:http://jsbin.com/eyarod/1Edit

2 个答案:

答案 0 :(得分:1)

在我的系统上,JSBin测试实际上与UPS一起运行:60和FPS:60几乎一直在运行(注意我在i7-2600k上使用chrome 25.0.1349.2 dev运行它,并且我打开了VSync ,这就是帧速率受限的原因。当我切换到另一个选项卡并返回时,FPS变得有点乱:我怀疑这与浏览器在选项卡未激活时限制setTimeout调用的方式有关。

更一般地说,您似乎试图将消息轮询忙等待循环塞进JavaScript,这是一种事件驱动的语言。我怀疑(但不确定),添加一个事件处理程序,用当前的x和y更新共享对象,然后有一个单独的循环以固定到监视器刷新率的速率更新屏幕将执行很多更好。

答案 1 :(得分:0)

  

现在,我知道requestAnimationFrame比setTimeout工作得更好,但理论上当前版本应该有效,除非我犯了一个根本性的错误。

允许用户代理等待额外的时间,只要他们喜欢,超过提供给setTimeout的毫秒参数(并且通常执行延迟执行的非实时API保证代码之前不运行您指定的延迟,但在指定的延迟后可能会运行任意时间)。此外,当一个this.ticks非常接近,但是小于一帧并且下一次调用是在触发两次更新的时候,跳过一个帧时,您可能会观察到一次更新调用时出现波动。

错误在于setTimeout永远无法提供requestAnimationFrame所做的保证。

如果您使用requestAnimationFrame并继续遇到问题,可以使用Chrome开发工具深入了解它们; here are slides about avoiding animation chop