对编写游戏循环感到困惑

Confusion with writing a game loop

我正在研究 2D 视频游戏框架,我以前从未编写过游戏循环。我看过的大多数框架似乎都实现了 drawupdate 方法。

对于my project,我实现了一个调用这两个方法的循环。我注意到在其他框架中,这些方法并不总是交替调用。有些框架 update 运行 比 draw 多得多。此外,大多数这些类型的框架将 运行 以 60FPS 的速度运行。我想我需要在这里睡一觉。

我的问题是,实现这种循环的最佳方法是什么?我是先调用 draw 然后调用 update,还是相反?就我而言,我正在围绕 SDL2 编写一个包装器,所以也许该库需要以某种方式进行设置?

这是我正在考虑的一些 "pseudo" 实现代码。

loop do
  clear_screen
  draw
  update
  sleep(16.milliseconds)
  break if window_is_closed
end

尽管我的项目是用 Crystal-Lang 编写的,但我更想寻找一个可以应用于任何语言的通用概念。

这取决于你想达到什么目的。有些游戏更喜欢游戏逻辑 运行 比帧速率更频繁(我相信 Source 游戏会这样做),对于某些游戏,您可能希望游戏逻辑 运行 不那么频繁(唯一的例子)我能想到的是一些多人游戏的服务器,比较出名的是守望先锋。

同样重要的是要考虑,这是分辨率的问题,而不是速度的问题。逻辑速率为 120 且帧速率为 60 的游戏不一定 运行 以 x2 速度运行,任何时候游戏逻辑中的关键操作都应相对于时钟*而不是 tic 速率完成,否则您的游戏会从字面上看如果帧渲染时间过长,则进入慢动作。

我建议像这样编写一个循环:

loop do
    time_until_update = (update_interval + time_of_last_update) - current_time
    time_until_draw = (draw_interval + time_of_last_draw) - current_time
    work_done = false

    # Update the game if it's been enough time
    if time_until_update <= 0
        update
        time_of_last_update = current_time
        work_done = true
    end

    # Draw the screen if it's been enough time
    if time_until_draw <= 0
        clear_screen
        draw
        time_of_last_draw = current_time
        work_done = true
    end

    # Nothing to do, sleep for the smallest period
    if work_done == false
        smaller = time_until_update

        if time_until_draw < smaller
            smaller = time_until_draw
        end

        sleep_for(smaller)
    end

    # Leave, maybe
    break if window_is_closed
end

您不想每帧等待 16 毫秒,否则如果帧需要很长时间才能完成,您可能会过度等待。 work_done 变量是为了让我们知道我们在循环开始时计算的间隔是否仍然有效,我们可能已经完成了 5 毫秒的工作,这会让我们完全无法入睡,所以在那种情况下我们去返回并计算新值。

* 你可能想把时钟抽象化,直接使用时钟会产生一些奇怪的效果,比如你保存游戏,你把上次使用神通的时间保存为时钟时间,它会立即当你载入存档时就结束了冷却时间,因为那已经过去了几分钟、几小时甚至几天。操作系统挂起的进程也存在类似问题。