对编写游戏循环感到困惑
Confusion with writing a game loop
我正在研究 2D 视频游戏框架,我以前从未编写过游戏循环。我看过的大多数框架似乎都实现了 draw
和 update
方法。
对于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 毫秒的工作,这会让我们完全无法入睡,所以在那种情况下我们去返回并计算新值。
* 你可能想把时钟抽象化,直接使用时钟会产生一些奇怪的效果,比如你保存游戏,你把上次使用神通的时间保存为时钟时间,它会立即当你载入存档时就结束了冷却时间,因为那已经过去了几分钟、几小时甚至几天。操作系统挂起的进程也存在类似问题。
我正在研究 2D 视频游戏框架,我以前从未编写过游戏循环。我看过的大多数框架似乎都实现了 draw
和 update
方法。
对于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 毫秒的工作,这会让我们完全无法入睡,所以在那种情况下我们去返回并计算新值。
* 你可能想把时钟抽象化,直接使用时钟会产生一些奇怪的效果,比如你保存游戏,你把上次使用神通的时间保存为时钟时间,它会立即当你载入存档时就结束了冷却时间,因为那已经过去了几分钟、几小时甚至几天。操作系统挂起的进程也存在类似问题。