游戏循环使我的浏览器崩溃
Game loop crashing my browser
我正在尝试编写我的第一个html5游戏。但是,游戏循环导致我的浏览器变得无响应(最终被浏览器关闭(。我创建了一个状态机:
while(state != State.EXIT){
switch(state){
case State.SPLASH:
break;
case State.HOW_TO:
break;
case State.PLAY:
oldTime=Date.now();
state=gameLoop();
break;
case State.GAME_OVER:
break;
default:
state=State.EXIT;
}
}
这似乎工作正常。那么,这里是游戏循环:
function gameLoop(){
var newTime=Date.now();
var delta=newTime-oldTime;
update(delta/1000);
render();
oldTime=newTime;
return state;
}
这是崩溃发生的地方。如果我取出 return 语句,它会返回 null 或任何 javascript 返回的内容。而且,这很好。它运行一次并退出。但是,如果我把它留在那里,这就是浏览器抓住的地方。更新功能使我的角色能够移动,渲染功能将一张图像绘制到屏幕上。非常简单的东西。
注意:如果这很重要,这是在画布元素中编写的。
溶液!我创建了一个 stateSelector(( 函数,其中包含上面的 switch 语句(没有 while(。但是,我没有使用 state=gameLoop,而是使用了 interval=setInterval(gameLoop, 1(。然后,当我想停止时,我使用 clearInterval(interval(,紧跟 stateSelector((。显然,如果我想更改状态,我会在调用 stateSelector 函数之前执行此操作。我可能会让它接受一个包含我想要进入的状态的参数,但这是一个我可以稍后评估的小更改。我只是想宣布我的解决方案,以防其他人遇到这种情况。
JavaScript 是单线程的,在所有常见的浏览器环境中(实际上(在 GUI 线程中运行。当你是JavaScript时,浏览器的UI在JavaScript完成运行之前不会更新。
您正在使用永远不会完成的while
循环,因此 UI 永远不会更新。要解决此问题,您需要稍微重组一下:渲染一个帧,然后告诉浏览器您想尽快渲染另一个帧;它可以更新 UI 并执行其他浏览器操作,然后它可以返回您以呈现另一个帧。
实现
有一个名为requestAnimationFrame
的实验性新功能可以做到这一点。由于它仍处于实验阶段,因此要使用它,您需要检查它的浏览器特定版本,或者如果它根本不可用,请提供回退。以下是特定于浏览器的版本的一些名称:
- 壁虎(火狐(的
mozRequestAnimationFrame
- WebKit
webkitRequestAnimationFrame
(Chrome 和 Safari( -
msRequestAnimationFrame
for Trident (Internet Explorer(
因此,如果有无前缀的requestAnimationFrame
可用,请使用该。如果它不可用,但前缀可用,请使用它。如果这些都不起作用,您可以使用回退:
function fallbackRequestAnimationFrame(func) {
setTimeout(func, 10); // Schedule func to be run in 10 milliseconds.
}
以下是在MDN上找到的代码的略微修改版本:
var myRequestAnimationFrame =
window.requestAnimationFrame
|| window.mozRequestAnimationFrame
|| window.webkitRequestAnimationFrame
|| window.msRequestAnimationFrame
|| fallbackRequestAnimationFrame;
一旦你弄清楚了可以使用哪个requestAnimationFrame
函数,你可以将你的游戏循环(似乎不是没有循环的gameLoop
函数,而是while
循环(更改为如下所示:
function runFrame() {
switch(state) {
// state handling code
}
if(state != State.EXIT) {
myRequestAnimationFrame(runFrame);
}
}
然后启动它:
runFrame();
我认为您可能需要某种暂停,如果您在没有暂停的情况下循环,它将一遍又一遍地消耗处理循环的所有 CPU,从而阻止页面呈现。
JavaScript 运行在浏览器用于呈现页面的同一线程上,因此如果您编写无限循环,浏览器永远不会获得刷新页面的控制权。(现代浏览器会检测"长时间运行"的循环,并为用户提供中止它们的机会,但这对游戏没有帮助。
您需要使用setTimeout()
或setInterval()
,并在以下方面有一些变化:
function gameLoop() {
// do calculations
// render
// etc
if (!gameOver)
setTimeout(gameLoop, 30);
}
gameLoop();
(注意:你原来的gameLoop()
函数实际上并没有循环 - 你的循环是在函数外部控制的 - 而我刚刚展示的确实循环。
setTimeout()
函数将函数排队等待稍后运行,然后立即继续下一行代码。当当前代码完成执行时,浏览器将重新获得控制权以更新显示等。然后在(大约(指定的间隔(以毫秒为单位(之后执行排队的函数。
上面的效果类似于递归调用,其中函数直接调用自身,除了同时使用 setTimeout()
将控制权交还给浏览器。
在gameLoop()
函数之外,您可以为键和/或鼠标事件定义事件处理程序,并让gameLoop()
使用这些更新变量来决定如何移动玩家的角色,就像我在回答另一个问题时所说的那样。
通常,程序员通过添加sleep(s)
或yield()
调用来使循环发挥良好作用,但由于 JavaScript 的事件驱动模型缺少这些,因此您可以改为用一个setInterval()
替换循环,该可以每隔指定的时间间隔(例如每 33 毫秒(调用包含类似循环体的函数,以获得 30 fps 的体验。
- 为什么这会造成一个无休止的循环并使我的浏览器崩溃
- javascript audio currentTime使一些浏览器崩溃
- 在浏览器崩溃之前检测到内存耗尽
- 输入输入前浏览器崩溃
- three.js内存泄漏/浏览器崩溃
- Ajax 请求仪表板页面上的浏览器崩溃
- set超时在“while”内会导致浏览器崩溃.我怎样才能避免它
- 设置设置间隔函数正在运行并导致浏览器崩溃
- 高效的Javascript组合函数,不会使浏览器崩溃
- 我的脚本使浏览器崩溃
- SlickGrid在选择大范围的行时使浏览器崩溃
- setInterval 在运行用户脚本时使我的浏览器崩溃
- 添加依赖项后浏览器崩溃
- Javascript/Jquery代码使我的浏览器崩溃
- 无限滚动会导致浏览器崩溃吗?
- 为什么这 while 循环会使浏览器崩溃
- 设置间隔导致浏览器崩溃
- JavaScript函数使我的浏览器崩溃
- 旧的JavaScript函数使浏览器崩溃
- 如何使用 IndexedDB 制作一个很长的字符串而不会使浏览器崩溃