4

这就是我处理游戏循环的方式:

while (running) {
    diff = duration_cast<milliseconds>(end - start).count();
    start = clock::now();

    dt = diff / (16.0);

    handleInput(); // get input
    update(dt); // game logic
    render(); // render game

    SDL_GL_SwapWindow(window); // swap frame buffer

    end = clock::now();
}

它旨在成为一个锁定到 60FPS 的固定时间步长游戏(它是对 SNES 游戏的重新模拟),但是它在我的 144hz 屏幕上以 144 个时间步长运行,这让它太快了。Vsync 不能解决这个问题,那有什么办法呢?

4

2 回答 2

4

以下是如何实现游戏循环的快速示例:

int32_t tickInteval = 1000/FPS; // frequency in Hz to period in ms
uint32_t lastUpdateTime = 0;
int32_t deltaTime = 0;
while (running) { // running condition
    uint32_t currentTime = SDL_GetTicks();
    deltaTime = currentTime - lastUpdateTime;

    int32_t timeToSleep = tickInteval - deltaTime;
    if(timeToSleep > 0)
    {
        SDL_Delay(timeToSleep); // energy saving
    }

    update(deltaTime); // game logic
    lastUpdateTime = currentTime;
}

我建议密切关注这个话题。


UPD。
人们可能会担心uint32_t溢出。是的,它会溢出。经过近两个月不间断的比赛(准确地说是 49.7 天)。那时会发生什么?currentTime将是一个非常小的正整数,lastUpdateTime将是一个非常大的正整数。但是二的减法无论如何都不会溢出。此外,如果差异不适合int32_t它,它将被模数环绕,UINT_MAX + 1从而产生一个小的正整数,这将是这两个值不同的确切刻度数(关于一个无符号溢出)。

于 2016-11-28T23:22:06.943 回答
1

SDL_Delay()和解决方案由于其不精确性而SDL_GetTicks()不太理想。


我们可以热循环:

while (1)
  if stopwatch.time() < 16666 // in usec
    break
  stopwatch.reset()
  input()
  update()
  render()

但这并不理想,因为它会占用可以放在其他地方或节省能源的 CPU 周期。


我们只能行动甚至帧

while (1)
  if frameCount = 1
    frameCount = 0
  else
    frameCount++
    input()
    update()
    render()
  waitForVSync // aka SDL_GL_SwapWindow(window)

但这只能让我们达到 72 fps。


如果我们需要尽可能接近 60 FPS,最好使用上述两种方法的混合:偶数帧上的热循环,直到达到 16.666 毫秒。

while (1)
  if frameCount = 1
    frameCount = 0
  else
    while stopWatch.time() < 16666 // in usec
      break
    stopwatch.reset()
    frameCount++
    input()
    update()
    render()
  waitForVSync // aka SDL_GL_SwapWindow(window)

这样做的好处是:您不必只是跳过奇数帧,您可以将它们用于各种事情。也许在奇数帧上更新,在偶数帧上渲染?在奇数帧上应用视频过滤器?很多可能性。

注意:您可能应该查看整体帧时间。此解决方案仅在帧速率至少为 120 fps 时有效。当帧速率低于 20 秒,并且您希望比操作系统线程提供的更准确时,热循环是您最好的选择。


另一种选择是利用 OS 调度,这往往比 OS 线程休眠更准确。但是,仍然不能保证所有系统的准确性。

于 2020-01-31T15:23:24.713 回答