有一个简单的项目,Box2D从事物理学,SFML绘制。在不同机器上具有不同性能速度的程序各不相同。如何设定固定速度?那么在强大的,平均的和弱的计算机上,程序的速度(对象的移动)是一样的吗? 我们需要让Box2D以相同的速度在不同的计算机上运行。它还给出坐标,SFML在给定坐标处绘制一个正方形。如何解决这个问题呢?
Constant Frame Rate是您正在寻找的。这是一个众所周知的问题。
如你所说,不同的机器将具有不同的处理器速度。这可能涉及具有更好硬件处理的计算机,您的游戏循环每秒更多次。
如果你想设置一个固定的帧速率,比方说,60 FPS,你所要做的就是(正如Öö Tiib指出的那样)限制你的更新每1/60秒进行一次更新。
这是一个常见的实现:
const sf::Time TimePerFrame = sf::seconds(1.f / 60.f); // 60 FPS
void run(){
sf::Clock clk;
sf::Time timeSinceLastUpdate = sf::Time::Zero;
while (_window.isOpen())
{
sf::Time dt = clk.restart();
timeSinceLastUpdate += dt;
while (timeSinceLastUpdate > TimePerFrame)
{
timeSinceLastUpdate -= TimePerFrame;
processInput();
update(TimePerFrame);
}
render();
}
}
}
这样,如果机器运行得更快,您将每1/60秒更新一次世界状态。
当程序启动时,timeSinceLastUpdate
取值0
,检查while
并获取自clk
创建(或重新启动)以来经过的时间,假设数字t1小于1/60秒。程序将它添加到timeSinceLastUpdate
并绘制场景,因为它是创建的(游戏的初始状态)。
你的while
的第二个循环,你再次获得经过的时间(自上次循环以来经过,因为你重新启动了时钟)。让我们这次打电话给t2。同样,这个数字小于1/60秒,但是,当你将它添加到timeSinceLastUpdate
时,两者都加起来大于1/60(所以timeSinceLastUpdate = t1 + t2 > TimePerFrame
)。这一次,您将进入第二个循环,并像1/60秒一样更新您的世界。你从timeSinceLastUpdate
减去1/60秒并再次检查。如果剩余超过1/60秒,您将再次更新,直到您保持最新状态。
如果你看起来很小心,你只需要调用update(TimePerFrame)
,所以你总是会以1/60秒的速度更新你的世界。
来自SFML Game Development Book。第1章:制作游戏勾号
Fixed time steps
到目前为止,我们提出的解决方案对于许多情况来说已经足够了。但它并不是一个完美的解决方案[...],因为每一帧都是独一无二的,你不能保证增量时间保持不变。考虑一个帧有时可能需要平均增量时间的三倍。这可能导致游戏逻辑中的严重错误,例如,当玩家移动三倍距离并穿过他通常会碰撞的墙壁时。这就是物理引擎期望修正增量时间的原因。
以下是描述我们所指的问题的图:
我们现在要做的是使用一种称为固定时间步骤的技术。我们编写的代码保证在任何情况下,无论发生什么,我们总是给更新函数提供相同的增量时间。如果你发现听起来很难,那与我们已经拥有的东西没什么大不同。我们只需要在代码中做一些记账,因为我们上次调用
update()
函数已经过了多长时间。代码类似于上面的代码,内部
while (timeSinceLastUpdate > TimePerFrame)
这种变化的实际效果是我们积累了变量
timeSinceLastUpdate
中经过了多长时间。当我们超过一帧所需的数量时,我们减去该帧的所需长度(即TimePerFrame
),并更新游戏。我们这样做,直到我们再次低于所需的金额。这解决了变量delta时间的问题,因为我们保证始终运行相同数量的帧。在您可以下载的应用程序中,通过使TimePerFrame
常数等于sf::seconds(1.f / 60.f)
,逻辑帧速率将设置为每秒60帧。