2013-05-28 41 views
6

我正在研究模拟引力的JavaScript游戏。它使用HTML5 canvas元素为行星绘制2D椭圆。我在谷歌浏览器中测试我的游戏。这里有一个链接到游戏:http://gravitygame.hostingsiteforfree.com/index.php?page=playHTML为什么canvas2d上下文不再填充椭圆?

截至5月24日,它的工作就好了。但是,Chrome从26.0.1410.64升级到27.0.1453.94后,填充的椭圆有时不会绘制。每次我加载我的游戏时都不会发生这种情况,我从来没有在本地运行时让它崩溃。

这里的游戏工作的截图:enter image description here

这里是一个说明它不填充的椭圆截图:enter image description here

我不能告诉发生了什么。我将包括绘制所有行星的循环部分。为了便于阅读,我对其进行了修改。

var i = bodies.length; 
    while(i--){ 
    var I = bodies[i]; 
    var planetRad = (I.width/2)*_scale; 
    if(_showTrails){ 
     //draw the planet's trail 
    } 
    if(//the planet is completely off the screen){ 
     //draw a red planet on the edge of the screen 
     ctx.beginPath(); 
     ctx.arc(nX, nY, 2.5, 0, TWOPI); 
     ctx.fillStyle = offScreenColor; 
     ctx.fill(); 
     ctx.strokeStyle = offScreenOutline; 
     ctx.stroke(); 
    } 
    else{ 
     //draw planet 
     ctx.beginPath(); 
     ctx.arc(nX, nY, (I.width/2)*_scale, 0, TWOPI); 
     ctx.closePath(); 
     ctx.fillStyle = I.bodyColor; 
     ctx.fill();  
    } 
    if(_showMotionVector){ 
     //draw a line from the center of a planet showing the direction and speed it's travelling 
     ctx.strokeStyle = motionColor; 
     ctx.beginPath(); 
     ctx.moveTo(I.getScX(), I.getScY()); 
     ctx.lineTo(I.motion.x * _scale * 12 + I.getScX(), I.motion.y * _scale * 12 + I.getScY()); 
     ctx.stroke(); 
    } 
} 

为什么它会突然中断?

+0

我遇到了类似的问题与IE浏览器,所以这里试试。如果你添加ctx.closePath();在示例代码中的第一个弧之后? – markE

+0

这没有奏效。每过一段时间,我仍然可以让它加载它,行星不会出现。 – Justin

+0

无法在Chrome或Canary(+其他浏览器)中重现(在FF平移不适用于我btw)。你如何触发动画循环? – K3N

回答

5

我看了一下你的在线代码,发现你在动画循环中使用了setInterval

这是最有可能的原因是如果代码是不能完成调用运行堆栈调用的风险Calcs(计算)等 - 的情况下,这意味着你可以有路径这么复位对方。

尝试先用setTimeout更换setInterval。当然,您将需要从代码中又重新触发了 - 更重要的是,把一切都放在一个函数具有的setTimeout在该函数结束,即:

function animate() { 
    //... calcs and redraws which you have in setInterval 
    setTimeout(animate, 0); 
} 
animate(); 

我用0超时这里这个测试。在任何情况下,setTimeout/setInterval都不会同步到屏幕刷新率。

如果那有效,那么你知道原因。下一步是用​​替换它,但让我知道它是怎么回事。

在试图说明这个问题,我们可以看一下这个图示中:

Working interval

每个块代表的功能的环内,以及一个环是一种颜色。请记住,在setInterval呼叫固定间隔而setTimeout调用相对当它叫。在这个例子中,功能在时间预算内执行,所以一切都很顺利。

在下一个示意图:

Stacking interval

的支出预算,以便setInterval再次调用外部和第一完成之前下一个呼叫排队至第二环。当队列在两次调用之间进行处理时,最终有可能在“同一时间”有两个函数在上下文中工作(或者与您预期的顺序不同)。

Javascript当然是单线程的,所以它们不会同时执行,但是其中一个被保持等待 - 如果在最后一个块有时间被调用之前调用下一个队列的第一个块,那么第一个块将修改上下文,甚至可能在调用上一次调用的最后一次调用之前更改路径。随着时间的推移,滞后将增加并且潜在地(除非一些额外的可用处理资源不时地解决队列 - 在繁忙的系统上这不太可能发生)随着更多堆叠发生而变得越来越差。

也就是说,在这种情况下,您可以在arc填充前将行添加到上下文中beginPath()

(希望做出任何意义......)

使用setTimeout会阻止这种在动画循环的所有调用返回的时候,才不会被执行。更好的选择是使用requestAnimationFrame,因为这将调用与屏幕刷新率同步,并且在可能的情况下。它更低级,因此也更高效。

另一条路径(无双关语)是使用Web-workers来进行计算。这将是多线程的,并且可以提高整体性能,因为Web工作人员不会影响UI线程。

+0

我对setTimeout()的初始测试似乎有效。不过,在我确信之前,我必须在几台电脑上试用它。我会让你知道结果。 – Justin

+1

迄今为止看起来不错。除了从setInterval切换到setTimeout之外,我必须在第一条评论中提到您所提到的更改。 – Justin