我使用Box2D和WebGL。 Box2D需要一个恒定的帧速率(它的“世界”更新的时间步长)。
function update(time) {//update of box2d world
world.Step(
1/60 // 1 / frame-rate
, 3 //velocity iterations
, 8 //position iterations
);
但我已经读过如下定义的requestAnimFrame是正确的方法。
requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
window.setTimeout(callback, 1000/60);
};
})();
requestAnimFrame没有给我一个恒定的帧速率,所以我的Box2D的变量是不同步的。
有解决方法吗?
[编辑]
John的(Cutch)解决方案在实施时如下所示:
function interpolate(dt) {
var t = dt/time_step;
body_coordinates = (1-t) * body_coordinates + t * next_body_coordinates;
}
var physicsDt = 0;
function tick() {
var time_now = new Date().getTime();
var dt = time_now - last_time; //Note that last_time is initialized priorly
last_time = time_now;
physicsDt += dt;
clear_the_screen();
requestAnimFrame(tick);
drawEverything();
if(physicsDt >= time_step) {
update();
physicsDt -= time_step;
}
interpolate(dt);
}
请注意,我的物理更新功能会注意next_attribue
已设置。
而且,在此之前调用物理update
,以使物理世界保持领先1帧。
结果
动画相当流畅,除非那些时候我能看到一些非常糟糕的跳跃和随机出现的微动作。
我认为解决方案中没有解决以下问题:
----> 1) dt
可能会大于time_step
:这会使dt/time_step
大于1,这会破坏插值方程。
当dt
始终大于time_step
时,问题会增加。
是否有可能克服时间间隔变得大于time_step
的问题?
我的意思是,即使我们将世界保持在渲染前一帧,如果时间间隔始终大于time_step
,那么“前方”帧也不会需要很长时间。
----> 2)想象dt
小于time_step
1 ms。然后,世界不会更新一次。现在进行插值并找到近似位置(它应该落后1毫秒)。
让我们说下次dt
和time_step
之间没有区别。
现在,考虑到dt
和time_step
相等,没有进行插值。那么,下一个绘制的是世界上的“前方”框架,对吗?(使用这些方程式,t = 1
)
但准确地说,渲染的世界应该是之前的1ms。
我的意思是,它落后于世界框架的1ms不应该消失。
但是使用t = 1
,绘制物理世界框架并忘记1ms。
我对代码或上述两点错了吗?
我请你澄清这些问题。
[编辑]
我在this评论中向there网页的作者询问了一种有效绘制多种形状的方法。
我学会了这样做:
我通过为每个形状保留单独的缓冲区并在初始化期间仅调用bufferData
,createBuffer
,bindBuffer
一次来保存bufferData
次调用。
enableVertexAttribArray
和vertexAttribPointer
(使用bindBuffer
)。
我的形状不随时间变化。 它们有很多种(如多边形,圆形,三角形),从头到尾都有。
答案 0 :(得分:6)
您必须将物理模拟步进时序与渲染vsync时序分离。最简单的解决方案是:
frameCallback(dt) {
physicsDt += dt;
if (physicsDt > 16) {
stepPhysics();
physicsDt -= 16;
}
renderWorld();
requestAnimFrame(frameCallback);
}
这里最大的问题是,有时你将使用过时的物理世界进行渲染,例如,如果physicsDt为15,则不会发生模拟更新,但是你的对象在那个时间点几乎会移动整个帧。您可以通过将物理1帧保持在渲染之前并在渲染器中线性插入对象位置来解决此问题。类似的东西:
var t = dt/16.0;
framePosition = (1-t) * previousFramePositions + (t) * nextFramePositions;
即使您的渲染与物理模拟不同步,您的对象也会平滑移动。如果您有任何问题,请告诉我。
约翰
答案 1 :(得分:1)
requestAnimFrame
并不是为了保证恒定的帧速率 - 它的设计使得浏览器只对它实际绘制的帧进行计算。
如果您想要/必须计算未绘制的帧,那么就不可能了。