3

我想在我的游戏引擎中实现确定性,以便能够保存和重放输入序列并使网络更容易。

我的引擎目前使用可变时间步长:每一帧我都会计算更新/绘制最后一帧所需的时间,并将其传递给我的实体的更新方法。这使得 1000FPS 游戏看起来和 30FPS 游戏一样快,但引入了不确定的行为。

一个解决方案可能是将游戏修复为 60FPS,但它会使输入延迟更多,并且不会获得更高帧速率的好处。

所以我尝试使用一个线程(它不断调用 update(1) 然后休眠 16 毫秒)并在游戏循环中尽可能快地绘制。它有点工作,但它经常崩溃,我的游戏变得无法播放。

有没有办法在我的游戏循环中实现线程以实现确定性,而无需重写所有依赖于引擎的游戏?

4

4 回答 4

5

您应该将游戏帧与图形帧分开。图形框架应该只显示图形,没有别的。对于重播,无论您的计算机能够执行多少图形帧(每秒 30 帧还是每秒 1000 帧)都无关紧要,重播计算机可能会以不同的图形帧速率重播它。

但是您确实应该修复游戏框架。例如每秒 100 个游戏帧。在游戏框架中执行游戏逻辑:与您的游戏(和重播)相关的内容。

只要不需要游戏帧,您的游戏循环就应该执行图形帧,因此,如果您将游戏固定为每秒 100 个游戏帧,则每个游戏帧为 0.01 秒。如果您的计算机只需要 0.001 秒即可在游戏帧中执行该逻辑,那么剩下的 0.009 秒用于重复图形帧。

这是一个小但不完整且不是 100% 准确的示例:

uint16_t const GAME_FRAMERATE = 100;
uint16_t const SKIP_TICKS = 1000 / GAME_FRAMERATE;

uint16_t next_game_tick;

Timer sinceLoopStarted = Timer(); // Millisecond timer starting at 0
unsigned long next_game_tick = sinceLoopStarted.getMilliseconds();

while (gameIsRunning)
{
    //! Game Frames
    while (sinceLoopStarted.getMilliseconds() > next_game_tick)
    {
        executeGamelogic();

        next_game_tick += SKIP_TICKS;
    }

    //! Graphical Frames
    render();
}

以下链接包含有关创建准确游戏循环的非常好的和完整的信息:

http://www.koonsolo.com/news/dewitters-gameloop/

于 2013-03-15T07:22:26.383 回答
4

为了在整个网络中具有确定性,您需要一个单一的事实点,通常称为“服务器”。游戏界有句俗话叫“客户在敌人手中”。确实如此。您不能相信客户端为公平游戏计算的任何东西。

例如,如果由于某些原因您的线程每秒仅更新 59 次而不是 60 次,您的游戏变得更容易,人们会发现。也许一开始他们甚至不会是恶意的。他们当时只是让他们的机器处于满负荷状态,而您的过程没有达到每秒 60 次。

一旦您拥有一个不关心图形或更新周期并以自己的速度运行的服务器(甚至可能作为单人游戏中的线程在进程中),它的确定性足以至少为所有玩家获得相同的结果。基于计算机不是实时的事实,它可能仍然不是 100% 确定性的。即使您告诉它更新每个 $frequence,它也可能不会,因为计算机上的其他进程负载过多。

服务器和客户端需要通信,因此服务器需要向每个客户端发送其状态的副本(出于性能考虑,可能是上一个副本的增量)。客户可以以可用的最佳速度绘制此副本。

如果您的游戏因线程而崩溃,也许可以选择将“服务器”实际退出进程并通过网络进行通信,这样您会很快发现哪些变量需要锁定,因为如果您只是将它们移动到另一个项目,您的客户端将不再编译。

于 2013-03-15T07:21:52.853 回答
2

将游戏逻辑和图形分离到不同的线程中。游戏逻辑线程应该以恒定的速度运行(例如,它每秒更新 60 次,如果您的逻辑不太复杂,甚至更高,以实现更流畅的游戏玩法)。然后,您的图形线程应始终尽可能快地绘制逻辑线程提供的最新信息,以实现高帧率。

为了防止绘制部分数据,您可能应该使用某种双缓冲,其中逻辑线程写入一个缓冲区,而图形线程从另一个缓冲区读取。然后每次逻辑线程完成一次更新时切换缓冲区。

这应该确保您始终充分利用计算机的图形硬件。当然,这确实意味着您要限制最低 cpu 速度。

于 2013-03-15T07:05:22.643 回答
0

我不知道这是否会有所帮助,但如果我没记错的话,Doom 存储了您的输入序列并使用它们来生成 AI 行为和其他一些东西。Doom 中的一个演示块将是一系列数字,它代表的不是游戏状态,而是你的输入。从那个输入,游戏将能够重建发生的事情,从而实现某种确定性......虽然我记得它有时会不同步。

于 2013-03-15T07:05:52.317 回答