游戏循环崩溃了我的浏览器

时间:2012-02-29 05:19:23

标签: javascript html5

我正在尝试编写我的第一个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返回的任何内容。而且,那没关系。它运行一次并退出。但是,如果我把它放在那里,这就是浏览器占用的地方。更新功能使我的角色能够移动,渲染功能将一个图像绘制到屏幕上。非常简单的东西。

注意:如果重要的话,这将写在canvas元素中。

解!我创建了一个stateSelector()函数,它包含上面的switch语句(没有while)。但是,我使用的是interval = setInterval(gameLoop,1),而不是state = gameLoop。然后,当我想要停止时,我使用clearInterval(interval),然后立即使用stateSelector()。显然,如果我想改变状态,我会在调用stateSelector函数之前这样做。我可能会接受一个包含我想要进入的状态的参数,但这是我稍后可以评估的一个小变化。我只想宣布我的解决方案,以防其他人遇到此问题。

4 个答案:

答案 0 :(得分:3)

JavaScript是单线程的,并且在所有常见浏览器环境中的GUI线程中运行(实际上)。当您使用JavaScript时,在JavaScript完成运行之前,浏览器的UI不会更新。

您正在使用永远不会完成的while循环,因此UI永远不会更新。要解决这个问题,你需要重新构建一个:渲染一个框架,然后告诉浏览器你想尽快渲染另一个框架;它可以更新UI并执行其他浏览操作,然后它可以返回给您渲染另一个框架。

实施

有一个名为requestAnimationFrame的实验性新功能可以做到这一点。由于它仍然是实验性的,要使用它,您需要检查它的浏览器特定版本,或者如果它根本不可用,则提供后备。以下是浏览器特定版本的一些名称:

  • mozRequestAnimationFrame适用于Gecko(火狐)
  • webkitRequestAnimationFrame适用于WebKit(Chrome和Safari)
  • msRequestAnimationFrame for Trident(Internet Explorer)

因此,如果有一个没有前缀的requestAnimationFrame可用,请使用它。如果没有,但是前缀是,请使用它。如果这些都不起作用,您可以使用后备:

function fallbackRequestAnimationFrame(func) {
    setTimeout(func, 10); // Schedule func to be run in 10 milliseconds.
}

以下是代码found on MDN的略微修改版本:

var myRequestAnimationFrame =
       window.requestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.msRequestAnimationFrame
    || fallbackRequestAnimationFrame;

一旦你弄清楚你可以使用哪个requestAnimationFrame函数,就可以改变你的游戏循环(这似乎不是gameLoop函数,它没有循环,而是{{ 1}} loop)看起来像这样:

while

然后开始:

function runFrame() {
    switch(state) {
        // state handling code
    }
    if(state != State.EXIT) {
        myRequestAnimationFrame(runFrame);
    }
}

答案 1 :(得分:0)

我认为你可能需要某种暂停,如果你循环没有暂停,它将消耗所有CPU反复处理循环,阻止页面呈现。

答案 2 :(得分:0)

通常情况下,程序员通过添加sleep(s)yield()调用来使循环播放变得很好,但由于javascript的事件驱动模型缺少这些调用,因此您可以使用可调用的setInterval()替换循环一个函数,包含每个指定间隔的循环体,比如每33毫秒一次,体验30 fps。

答案 3 :(得分:0)

JavaScript在浏览器用于呈现页面的同一个线程上运行,因此如果您编写无限循环,浏览器将永远无法控制回刷新页面。 (现代浏览器会检测“长时间运行”循环并为用户提供中止它们的机会,但这对您的游戏没有帮助。)

您需要使用setTimeout()setInterval()以及以下内容的一些变体:

function gameLoop() {
   // do calculations
   // render
   // etc

   if (!gameOver)
       setTimeout(gameLoop, 30);
}

gameLoop();

(注意:你的原始gameLoop()函数实际上并没有循环 - 你的循环是在函数之外控制的 - 而我刚刚显示的函数循环。)

setTimeout()函数将函数排队等待稍后运行,然后立即继续下一行代码。当前代码完成执行浏览器然后获得控制权以更新显示等。然后在(大约)指定的间隔(以毫秒为单位)后执行排队的函数。

上面的效果类似于一个函数直接调用自身的递归调用,除非使用setTimeout()在此期间将控制权交还给浏览器。

外部 gameLoop()函数,您可以为键和/或鼠标事件定义事件处理程序,并拥有gameLoop()将用于决定如何的更新变量,例如,移动玩家的角色,就像我在this answer to another question中说的那样。