为什么开发游戏需要一个“主”游戏循环?
我发现大多数游戏开发需要一个主要的游戏循环,但我不知道为什么这是必要的。 难道我们不能实现一个事件监听器并响应每个用户操作吗? animation(等)可以在事件发生时播放。
主游戏循环的目的是什么?
你“需要一个循环,否则什么叫事件监听器”的论点并不成立。 不可否认,在任何主stream操作系统上,确实有这样一个循环,而事件监听器也是这样工作的,但是完全有可能build立一个没有任何循环的中断驱动系统。
但是你仍然不想这样构build游戏。
使循环成为最吸引人的解决scheme的是,循环成为实时编程中被称为“循环执行”的循环。 这个想法是,你可以使各个系统活动的相对执行率相互确定。 循环的总体速率可以由一个定时器来控制,这个定时器最终可能是一个中断,但是对于现代操作系统来说,你可能会看到这个中断的证据是等待一个信号量(或者其他同步机制)的代码你的“主循环”的一部分。
那么为什么你需要确定性的行为? 考虑你的用户input和坏人AI的相对处理速度。 如果你把所有东西都放到纯粹基于事件的系统中,那么不能保证AI不会比你的用户获得更多的CPU时间,反之,除非你有一些控制线程优先级的东西,即使如此,很难保持时间一致。
然而,把所有的事情都放在一个循环中,而且你保证你的AI时间线将在你的用户时间的固定关系上进行。 这是通过从你的循环中发出一个呼叫给AI给出一个时间片来决定做什么,调用你的用户input例程,轮询input设备以找出你的用户想要的行为,以及叫出来做你的渲染。
有了这样一个循环,你必须注意,你没有花费更多的时间来处理每一个传球,而不是实际进行。 如果你想循环100Hz,所有的循环处理最好在10毫秒以内完成,否则你的系统会变干。 在实时编程中,它被称为超时你的时间框架。 一个好的系统可以让你监视你是否超出距离,然后你可以减轻处理负荷,但是你认为合适。
一个事件监听器也依赖于一些调用循环,不pipe你是否看到它。 还有谁会打电话给听众?
构build一个明确的游戏循环可以让你完全掌握正在发生的事情,所以不会依赖于某些工具包/事件处理库在其事件循环中所做的事情。
所有types的游戏都需要一个专门的主游戏循环。
由于频繁的对象更新和游戏input精度,动作游戏需要这样一个循环。
另一方面,我实施了一个扫雷游戏,我用了窗口
消息的通知。
游戏循环(高度简化如下)
初始化 做 input 更新 给予 循环 清理
这将发生在游戏画出的每一帧。 所以对于以60fps运行的游戏,每秒执行60次。
这意味着游戏运行平稳,游戏保持同步,并且每个周期的更新/绘制经常发生。 animation只是一个窍门,物体在不同位置之间移动,但是当播放速度足够快时,它们似乎在这些位置之间移动。
如果您只是更新用户input,游戏只会在用户提供input时作出反应。 其他游戏组件如AI游戏对象不会自行反应。 因此循环是更新游戏的最简单和最好的方式。
这是因为当前的操作系统并不完全基于事件。 即使事件通常被表示为事件,但仍然需要创build一个循环,等待下一个事件并无限期地进行处理(例如Windows事件循环)。 Unix信号可能是您在操作系统级别上最接近事件的事情,但是对于这样的事情,它们并不是非常有效。
实际上,正如其他人所指出的那样,需要一个循环。
但是,你的想法在理论上是合理的。 你不需要循环。 您需要基于事件的操作。
在一个简单的层面上,你可以将CPU概念化为多个定时器;
- 一个在60Hz的上升沿触发,并且调用blitting码。
- 另一个可能是在60kHz的滴答声,并将游戏世界中的对象的最新更新呈现给阻击器缓冲器。
- 另一个可能是在10kHz的滴答声,并从用户采取的input。 (相当高的分辨率,大声笑)
- 另一个可能是游戏“心跳”,并在60MHz的蜱; AI和物理学可能会在心跳的时候运作。
当然,这些计时器可以调整。
实际上,将会发生什么事情,你会这样(有些被忽略):
void int_handler1(); //... int main() { //install interrupt handlers //configure settings while(1); }
任何可以无限期地坐在那里并响应用户input的程序都需要一些循环。 否则就会到达程序结束并退出。
主循环调用事件监听器。 如果你有足够的幸运有一个事件驱动的操作系统或窗口pipe理器,事件循环驻留在那里。 否则,您将编写一个主循环来调解基于I / O, poll
或select
的系统调用接口与传统事件驱动的应用程序之间的“阻抗不匹配”。
PS由于您使用函数式编程标记了您的问题,因此您可能需要查看Functional Reactive Programming ,它能够将高级抽象与低级的基于事件的实现相结合。
游戏需要实时运行,所以如果它连续运行在一个CPU /内核上,效果最好。 事件驱动的应用程序通常会在队列中没有事件时将CPU传递给另一个线程。 在CPU切换回你的进程之前,可能还有相当长的一段时间。 在一场比赛中,这将意味着短暂的摊位和动摇的animation。
两个原因 –
即使事件驱动的系统通常也需要一个从某种types的队列中读取事件的循环,并将它们分派给一个处理程序,所以最终会在窗口中产生一个事件循环,而且可能会很好地扩展它。
为了animation的目的,您甚至需要处理某种animation的每一帧。 你当然可以用定时器或某种闲置事件来做到这一点,但是你可能最终会在某种循环中创build这些循环,所以直接使用循环会更容易。
我见过使用事件处理这些事件的系统,它们有一个监听器,监听每帧开始时分派的事件。 他们在内部仍然有一个小小的游戏循环,但它不仅仅是处理窗口系统事件,而且还创build帧事件,
游戏的本质是它们通常是模拟的,不仅仅是基于外部事件而是基于内部过程来进行反应。 您可以通过循环事件而不是轮询来表示这些内部stream程,但它们实际上是等价的:
schedule(updateEvent, 33ms) function updateEvent: for monster in game: monster.update() render()
VS:
while 1: for monster in game: monster.update() wait(33ms) render()
有趣的是, pyglet实现了基于事件的方法,而不是更传统的循环。 虽然这很有效,但有时会导致性能问题或由于时钟分辨率,vsync等原因而导致的不可预知的行为。循环更易于理解(除非来自专门的Web编程背景,可能)。