我正在开发一款模拟引力的javascript游戏。它使用HTML5 canvas元素为行星绘制2D椭圆。我在谷歌浏览器中测试我的游戏。这是游戏的链接:http://gravitygame.hostingsiteforfree.com/index.php?page=playHTML
直到5月24日,它运作得很好。但是,Chrome从26.0.1410.64升级到27.0.1453.94后,有时不会绘制填充的椭圆。每次我加载游戏时都不会发生这种情况,而且我在本地运行时从未让它破裂。
以下是游戏的截图:
这是一个屏幕截图,显示它没有填充省略号:
我不知道发生了什么。我将包括绘制所有行星的循环部分。为了便于阅读,我对其进行了修改。
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 :(得分:5)
我查看了您的在线代码,发现您正在使用setInterval
进行动画循环。
这很可能是代码无法完成调用计算等的原因。您冒着堆叠调用的风险 - 因为上下文意味着您可以拥有相互重置的路径。
首先尝试将setInterval
替换为setTimeout
。你当然需要在代码中再次重新触发它 - 更好的是,在函数结尾处将所有内容放入一个带有setTimeout的函数中,即:
function animate() {
//... calcs and redraws which you have in setInterval
setTimeout(animate, 0);
}
animate();
我在此测试使用0表示超时。在任何情况下,setTimeout/setInterval
都不会同步到屏幕刷新率。
如果有效,那么你知道原因。下一步是用requestAnimationFrame
替换它,但请告诉我它是怎么回事。
为了说明问题,我们可以看一下这个例子:
每个块表示循环内的一个函数,一个循环是一种颜色。请注意,setInterval
以固定的间隔调用,而setTimeout
调用 relative 时调用它。在这个例子中,函数在时间预算内执行,因此一切顺利。
在下一个插图中:
支出超出预算,因此再次调用setInterval
并在第一个循环结束之前排队第二个循环。当在调用之间处理队列时,你最终冒着在"同一时间"处理上下文的两个函数的风险。 (或者以不同于你预期的顺序排列。)
Javascript当然是单线程的,因此它们不会同时执行,但是一个等待 - 如果在最后一个块有时间被调用之前调用下一个队列的第一个块,那么第一个块将修改上下文,甚至可能在调用上一次调用的最后一次调用之前更改路径。随着时间的推移,滞后将会增加并且潜在地(除非一些额外的可用处理资源现在解决队列,然后 - 在繁忙的系统上这不太可能发生)随着更多堆叠的发生而变得越来越糟。
即,在这种情况下,您可以在beginPath()
填充之前将行添加到arc
的上下文中。
(希望有任何意义......)
使用setTimeout
可以防止这种情况,因为它不会在动画循环中的所有调用都返回之前执行。更好的选择是使用requestAnimationFrame
,因为这将尽可能与屏幕刷新率同步调用。它更低级,因此也更有效率。
另一条路径(没有双关语)是使用Web-workers进行计算。这将是多线程的,并且可以提高整体性能,因为Web工作者不会影响UI线程。