javascript游戏中的异步/可变帧率

asynchronous / variable framerate in javascript game

本文关键字:帧率 异步 游戏 javascript      更新时间:2023-09-26

这可能是一个愚蠢的/以前回答过的问题,但它一直困扰着我和我的朋友一段时间,我一直无法找到一个好的答案。

现在,我让我所有的JS画布游戏运行滴答。例如:

function tick(){
//calculate character position
//clear canvas
//draw sprites to canvas
if(gameOn == true)
    t = setTimeout(tick(), timeout)
}

这对于高端系统上cpu便宜的游戏来说很好,但是当我试图每隔一秒画更多的时候,它开始以慢动作运行。所以我的问题是,我如何保持x,y位置和命中检测计算全速进行,同时允许可变帧率?

旁注:我曾尝试使用requestAnimationFrame API,但说实话,它有点令人困惑(不是所有的好教程),虽然它可能会加快你的处理,它并不能完全解决问题。

RequestAnimationFrame使产生很大的差异。这可能是解决你问题的办法。你还可以做两件事:设置第二个滴答系统来处理它的模型方面,例如命中检测。一个很好的例子就是PhysiJS是如何做到这一点的。它更进了一步,使用了一些新浏览器的一个功能,叫做web worker。它允许您使用第二个CPU核心。John Resig有一个很好的教程。但是要注意,它很复杂,没有得到很好的支持(因此有很多bug,经常崩溃)。

真的,请求动画帧是非常简单的,它只是几行,一旦你设置好,你可以忘记它。它不应该改变任何现有的代码。理解代码的作用有点困难,但是您可以对示例中的setTimeout代码进行剪切和替换。如果你问我,setTimeout也一样复杂!它们实际上做同样的事情,除了setTimeout有一个延迟时间,而requestAnimationFrame没有——它只是在准备好时调用你的函数,而不是在一段设定的时间之后。

您实际上并没有使用刻度。发生的是你不断地调用tick(),一遍又一遍。你需要删除(),只留下setTimeout(tick,timeout);。我个人喜欢使用arguments.callee来明确地声明一个函数调用自己(从而消除了知道函数名称的依赖关系)。

话虽如此,我在执行可变帧率时喜欢做的是尽可能地简化底层引擎。例如,要让一个球在墙上弹跳,我要检查从球的前一个位置到下一个位置的线是否碰到了墙,如果是,什么时候。

也就是说,你需要小心,因为一些浏览器停止所有的JavaScript执行时,一个链接菜单(或任何其他菜单)被打开,所以你可能以几秒甚至几分钟的间隔结束两个"帧"之间。我个人认为基于帧的计时在大多数情况下是可行的。

正如Kolink提到的。setTimeout看起来像个bug。假设这只是一个拼写错误,而不是一个bug,我想说,它不太可能是动画本身(也就是说,DOM更新),这是真正减慢你的代码。

多一点多少钱?我在一台1.2GHz Atom上网本(我在我最慢的机器上使用的最慢的浏览器,VMWare是因为我使用Linux)上的IE7上,在屏幕上同时动画了数百个元素,效果很好。

根据我的经验,当动画元素的数量增加时,如果没有正确地进行命中检测会导致最大的减速。这是因为一个朴素的实现本质上是指数的(它会尝试做n^n比较)。解决这个问题的方法是过滤掉比较,以避免不必要的比较。

在游戏引擎中最常见的方法之一便是将世界地图分割成更大的网格集。然后,你只能对同一网格中的物品进行命中检测(如果你想更准确的话,也可以对相邻的网格进行命中检测)。这大大减少了您需要进行的比较次数,特别是当您有很多字符时。