对于具有不同硬件容量的不同设备,如何在浏览器繁忙或空闲的情况下获得更好的动画效果。
我尝试了很多方法,仍然无法找到让游戏显示更好动画的正确方法。
这是我试过的:
var now;
var then = Date.now();
var delta;
window.gamedraw = function(){
now = Date.now();
delta = now - then;
if(delta > 18){
then = now - (delta % 18);
game_update();
}
}
window.gameloop = setInterval(window.gamedraw,1);
18是更新游戏的间隔值,但是当浏览器忙时,这个间隔不好,需要降低。即使浏览器闲置或忙碌,如何在动态上获得更好的动画?
我认为间隔值是问题所在,因为如果间隔值较低则游戏动画速度非常快,如果此值为18则游戏动画效果不错,但浏览器繁忙时却不行,我不知道如何改变它的恐怖。
答案 0 :(得分:3)
要获得流畅的动画,您必须:
•在屏幕上同步。
•计算游戏中经过的时间
•仅使用此游戏时间进行动画处理。
使用requestAnimationFrame(rAF)完成与屏幕的同步 当你写:
requestAnimationFrame( myCalbBack ) ;
您正在注册myCalbBack
以便一次,下一次可以使用该画面。
(如果你知道双缓冲(画布总是双缓冲),这次是下一次GPU将绘图缓冲区与显示缓冲区交换。)
另一方面,如果您不使用rAF但是使用间隔/超时来安排抽奖,那么将会发生的抽奖实际上不会显示直到下一次显示刷新。从现在到16.6毫秒之后的任何时间都可能发生(在60Hz显示屏上)。
下面有一个20毫秒的间隔和一个16毫秒的屏幕,你可以看到实际显示的图像可能是16毫秒距离或2 * 16毫秒距离 - 绝对不是20毫秒 - 肯定 - 。你不能从区间回调中知道实际抽签何时显示。由于rAF和Intervals都不是100%准确,你甚至可以有3帧增量。
现在,您与屏幕同步,这是一个坏消息:requestAnimationFrame不会完全定期打勾。由于各种原因,两帧之间的经过时间可能会改变1ms或2,甚至更多。因此,如果您在每个帧使用固定运动,您将在不同时间内移动相同距离:速度始终在变化。
(探索:每个rAF +10 px,
16.6显示 - >> rAF时间为14,15,16或17毫秒
- >表观速度从0.58到0.71 px / ms不等。 )
答案是衡量时间......并使用它! 希望requestAnimationFrame为您提供当前时间,因此您甚至不必使用Date.now()。次要的好处是,对于具有准确计时器(Chrome桌面)的浏览器,这个时间非常准确 下面的代码显示了如何知道自上一帧以来经过的时间,并计算应用程序时间:
function animate(time) {
// register to be called again on next frame.
requestAnimationFrame(animate);
// compute time elapsed since last frame.
var dt = time-lastTime;
if (dt<10) return;
if (dt >100) dt=16; // consider only 1 frame elapsed if unfocused.
lastTime=time;
applicationTime+=dt;
//
update(dt);
draw();
}
var lastTime = 0;
var applicationTime = 0;
requestAnimationFrame(animate);
现在最后一步是始终在所有公式中使用时间。所以不要做
x += xSpeed ;
你必须这样做:
x += dt * xSpeed ;
现在不仅会考虑帧间的微小变化,而且无论设备的屏幕如何(20Hz,50Hz,60Hz,......),你的游戏都将以相同的视速运行。
我做了一个小型演示,您可以选择同步,如果使用固定时间,您将能够判断差异:
http://jsbin.com/wesaremune/1/
答案 1 :(得分:0)
您应该使用requestAnimationFrame
代替setInterval
。
后者实际上正在讨论您的方法(使用增量时间),然后将动画帧作为更可靠的替代方案。
第一篇文章确实是一个很好的资源。对于初学者来说,你的间隔时间为18毫秒,你的目标显然是接近60 fps。这实际上是requestAnimationFrame
的默认值,所以你不需要写任何特别的东西:
function gamedraw() {
requestAnimationFrame(gamedraw); //self-reference
game_update(); //your update logic, propably needs to handle time intervals internally
}
gamedraw(); //this starts the animation
如果要明确设置更新间隔,可以将requestAnimationFrame
包裹在setInterval
内,如下所示:
var interval = 18;
function gamedraw() {
setTimeout(function() {
requestAnimationFrame(gamedraw);
game_update(); //must handle time difference internally
}, interval);
}
gamedraw();
请注意,game_update()
函数必须跟踪上次调用的时间,以便...如果必须跳过一个帧,请将所有内容移动到正常位置两次。
实际上,这意味着您可以(并且可能应该)重构您的game_update()
函数,以将实际作为参数传递的时间取而代之,而不是在内部确定。 (没有功能差异,它只是更好,更清晰的代码IMO,因为它不会隐藏时间魔法。)
var time;
function gamedraw() {
requestAnimationFrame(gamedraw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now; //reset the timer
game_update(dt); //update with explicit and variable time step
}
gamedraw();
(这里我再次删除了显式帧。)
尽管如此,我还是建议您 read the first article ,因为它还处理我尚未进入的跨浏览器问题。
答案 2 :(得分:-1)
您应该使用requestAnimationFrame
。它将排队回调以在下次浏览器呈现帧时运行。要实现持续更新,请递归调用更新函数。
var update = function(){
//做东西
requestAnimationFrame(更新)
}