我正在使用JavaScript和canvas
元素创建一些简单的用户控制模拟。
目前我有一个单独的更新循环(使用setTimeout
)和渲染循环(使用requestAnimationFrame
)。
使用时间增量来缩放更新,因此在这种意义上,一致性并不重要。原因是我不希望渲染循环中的任何hick-up吞下用户输入或者使模拟响应性降低。
更新循环可能会以较低(但希望是固定的)帧速率运行。
这是JavaScript中的一个好习惯,还是有任何明显的陷阱?我希望update
循环将获得优先权,但我对事件循环的理解可能有点过分了。 (在最坏的情况下,VM实现之间的行为不同。)
示例代码:
function update() {
// Update simulation and process input
setTimeout(update, 1000 / UPDATE_RATE);
}
function render() {
// Render simulation onto canvas
requestAnimationFrame(render);
}
function init() {
update();
render();
}
答案 0 :(得分:-1)
Isaac Sukin在Game Development with Three.js中解决了这些问题。它涵盖了低渲染帧率的情况,这是该问题的主要关注点:
[...]在低帧率和高速度下,你的物体每帧都会移动很长的距离,这会导致它做一些奇怪的事情,比如穿过墙壁。
它还涵盖了相反的情况,具有高渲染帧速率和相对慢的物理计算:
在高帧速率下,计算物理可能需要比帧之间的时间长,这将导致应用程序冻结或崩溃。
此外,它还涉及决定论的概念,这在多人游戏中变得很重要,而游戏则依赖于它来重放或反作弊机制:
此外,我们希望完美再现。也就是说,每次我们使用相同的输入运行应用程序时,我们都希望输出完全相同。如果我们有可变的帧增量,那么即使在正常的帧速率下,由于累积的舍入误差,我们的输出也会分散程序运行的时间。
建议不要运行多个循环,因为这会严重且难以调试性能影响。取而代之的是采用在渲染循环中累积时间增量的方法,直到达到固定的预设大小,此时将其传递到物理循环进行处理:
更好的解决方案是将物理更新时间步骤与帧刷新时间步骤分开。物理引擎应该接收固定大小的时间增量,而渲染引擎应该确定每帧应该发生多少次物理更新。
以下是一些示例代码,显示了JavaScript中的最低实现:
var INVERSE_MAX_FPS = 1 / 60;
var frameDelta = 0;
var lastUpdate = Date.now();
function render() {
// Update and render simulation onto canvas
requestAnimationFrame(render);
var now = Date.now();
frameDelta += now - lastUpdate;
lastUpdate = now;
// Run as many physics updates as we have missed
while(frameDelta >= INVERSE_MAX_FPS) {
update();
frameDelta -= INVERSE_MAX_FPS;
}
}
function init() {
render();
}
使用以下代码,无论自上次渲染帧后多长时间,都将处理所需的物理更新。任何剩余时间增量将被转移到下一帧。
请注意,目标最大FPS可能需要根据模拟运行的速度进行调整。