题
在开发游戏一段时间后,我接触到了可变帧速率(您可以计算出自上次更新以来已经过去了多少时间,并相应地更新演员运动)和固定帧速率(您可以计算出多少时间)已经过去了,选择是勾选固定的时间还是休眠直到下一个窗口到来)。
哪种方法最适合特定情况?请考虑:
- 满足不同系统规格;
- 易于开发/维护;
- 易于移植;
- 最后的表演。
解决方案
似乎大多数 3D 开发者更喜欢可变 FPS:Quake、Doom 和 Unreal 引擎都会根据系统性能进行缩放。
- 至少你必须补偿太快的帧速率(不像 80 年代的游戏在 90 年代运行,太快了)
- 无论如何,你的主循环应该通过时间步进行参数化,只要它不是太长,像 RK4 这样的像样的积分器应该能够顺利地处理物理。某些类型的动画(关键帧精灵)可能很难参数化。网络代码也需要智能,例如避免拥有更快机器的玩家发射太多子弹,但无论如何都需要进行这种限制以补偿延迟(动画参数化也将有助于隐藏网络延迟)
- 每个平台的计时代码都需要修改,但这是一个小的本地化更改(尽管有些系统很难实现极其准确的计时,Windows、Mac、Linux 似乎还可以)
- 可变帧速率可实现最佳性能。固定帧速率可以实现一致的性能,但永远不会在所有系统上达到最大值(这似乎是任何严肃游戏的阻碍)
如果您正在编写一款性能很重要的网络 3D 游戏,我不得不说,咬紧牙关并实现可变帧速率。
如果这是一款 2D 益智游戏,您可能可以使用固定的帧速率,也许可以针对超慢的计算机和明年的模型进行稍微参数化。
其他提示
我倾向于使用可变帧率模型,但在内部某些系统是按固定时间步长进行勾选的。通过使用时间累加器可以很容易地做到这一点。物理系统最好在固定时间步长上运行,并在必要时每帧多次勾选,以避免稳定性损失并保持模拟顺利。
一些代码来演示累加器的使用:
const float STEP = 60.f / 1000.f;
float accumulator = 0.f;
void Update(float delta)
{
accumulator += delta;
while(accumulator > STEP)
{
Simulate(STEP);
accumulator -= STEP;
}
}
这无论如何都不完美,但提出了基本思想——有很多方法可以改进这个模型。显然,当输入帧速率非常慢时,需要解决一些问题。然而,最大的优点是,无论增量有多快或多慢,模拟都会在“玩家时间”内以平稳的速度移动——这是用户会察觉到任何问题的地方。
一般来说,我不会涉及图形和音频方面的事情,但我认为它们不像物理、输入和网络代码那样受到影响。
作为一名用户,我希望更频繁地看到的一个选项是当帧速率变化超出特定范围时动态更改细节级别(广义上,而不仅仅是技术意义上)。如果您以 5FPS 渲染,请关闭凹凸贴图。如果您以 90FPS 进行渲染,请稍微增加一些附加功能,并为用户提供一些更漂亮的图像来浪费他们的 CPU 和 GPU。
如果做得正确,用户应该可以从游戏中获得最佳体验,而无需进入设置屏幕并自行调整,并且作为关卡设计师,您应该不必担心在不同场景中保持多边形数量相同。
当然,我是作为一名游戏用户这么说的,而不是一个严肃的人——我从未尝试过编写一款不平凡的游戏。
我在可变长度帧时间方面遇到的主要问题是浮点精度,可变帧时间可能会让您大吃一惊。
例如,如果您将帧时间 * 速度添加到某个位置,并且帧时间变得非常小,并且位置较大,则您的对象可能会减慢或停止移动,因为所有增量都因精度而丢失。您可以使用单独的误差累加器来补偿这一点,但这很痛苦。
固定帧时间(或至少是帧长度的下限)可以让您控制需要考虑的 FP 错误量。
我的经验相当仅限于一些简单的游戏(使用 SDL 和 C++ 开发),但我发现实现静态帧速率非常容易。您正在开发 2d 或 3d 游戏吗?我认为更复杂的 3D 环境会从可变帧速率中受益更多,并且难度会更大。