我发现大多数游戏开发需要一个主游戏循环,但我不知道为什么它是必要的。我们不能实现事件监听器并响应每个用户操作吗?然后可以在事件发生时播放动画(等)。
主游戏循环的目的是什么?
答案 0 :(得分:80)
你“需要循环,因为否则调用事件监听器”的论点并不成立。不可否认,在任何主流操作系统中,你确实有这样的循环,并且事件监听器确实以这种方式工作,但是完全有可能使中断驱动的系统在没有任何类型的循环的情况下工作。
但你仍然不想以这种方式构建游戏。
使循环成为最具吸引力的解决方案的是,您的循环变为实时编程中被称为“循环执行”的循环。这个想法是你可以使各种系统活动的相对执行率相互确定。循环的整体速率可以由定时器控制,并且该定时器最终可能是一个中断,但是对于现代操作系统,您可能会看到该中断的证据作为等待信号量(或其他一些同步机制)的代码你的“主循环”的一部分。
那么为什么你想要确定性的行为呢?考虑用户输入和坏人AI的相对处理率。如果你把所有东西都放在一个纯粹的基于事件的系统中,那么不能保证AI不会比你的用户获得更多的CPU时间,反之亦然,除非你对线程优先级有一些控制权,即便如此,你也是容易让时间保持一致。
然而,将所有内容放在一个循环中,您可以保证您的AI时间线将以与用户时间相关的固定关系进行。这可以通过从循环中调出来为AI提供一个时间片来决定做什么,调用用户输入例程,轮询输入设备以了解用户想要表现的行为,以及呼吁做你的渲染。
有了这样一个循环,你必须注意到你没有花费更多的时间来处理每个传球,而不是实际实时传递。如果你试图以100Hz的频率循环你的循环,你所有的循环处理最好在10毫秒内完成,否则你的系统会变得不稳定。在实时编程中,它被称为超越你的时间范围。一个好的系统可以让你监控你超越的程度,然后你就可以减轻你认为合适的处理负荷。
答案 1 :(得分:16)
无论您是否看到,事件侦听器还依赖于某些调用循环。还有谁会打电话给听众?
构建一个明确的游戏循环使您可以绝对控制正在发生的事情,因此您不会依赖于某些工具包/事件处理库在其事件循环中所做的事情。
答案 2 :(得分:7)
游戏循环(高度简化如下)
initialise do input update render loop clean up
这将在游戏绘制的每一帧发生。因此对于以60fps运行的游戏,每秒执行60次。
这意味着游戏运行顺畅,游戏保持同步,每个周期的更新/抽取频繁发生。动画只是一种伎俩,物体在不同位置之间移动,但是当它们播放得足够快时,它们似乎在这些位置之间移动。
如果您只更新用户输入,游戏只会在用户提供输入时做出反应。其他游戏组件,如A.I游戏对象不会自行反应。因此循环是更新游戏的最简单和最好的方式。
答案 3 :(得分:6)
所有类型的游戏都需要专门的主游戏循环。
由于频繁的对象更新和游戏输入精度,动作游戏需要这样的循环。
另一方面,我实施了扫雷游戏,并使用了窗口
通知的消息。
答案 4 :(得分:4)
这是因为当前的操作系统不是完全基于事件的。即使事物通常表示为事件,您仍然需要创建一个循环,等待下一个事件并无限期地处理它(例如Windows事件循环)。 Unix信号可能是你在操作系统级别上接近事件的最接近的事情,但是对于像这样的事情它们并不是真的有效。
答案 5 :(得分:3)
实际上,正如其他人所指出的那样,需要一个循环。
但是,你的想法在理论上是合理的。你不需要一个循环。您需要基于事件的操作。
在简单的层面上,您可以将CPU概念化为具有多个计时器;
当然可以调整这些计时器。
实际上,将要发生的事情是你会(有点省略)这样:
void int_handler1();
//...
int main()
{
//install interrupt handlers
//configure settings
while(1);
}
答案 6 :(得分:2)
任何可以无限期地坐在那里并响应用户输入的程序都需要某种循环。否则它只会到达程序结束并退出。
答案 7 :(得分:2)
主循环调用事件监听器。如果你有幸拥有一个事件驱动的操作系统或窗口管理器,那么事件循环就在那里。否则,您编写一个主循环来调解基于I / O,poll
或select
的系统调用接口与传统的事件驱动应用程序之间的“阻抗不匹配”。 / p>
P.S。由于您使用函数式编程标记了您的问题,因此您可能需要查看Functional Reactive Programming,这可以很好地将高级抽象连接到基于事件的低级实现。
答案 8 :(得分:2)
游戏需要在real-time中运行,因此如果它在一个CPU /核心上连续运行,效果最佳。当队列中没有事件时,事件驱动的应用程序通常会将CPU输出到另一个线程。在CPU切换回您的进程之前可能需要等待很长时间。在游戏中,这意味着短暂的停顿和抖动动画。
答案 9 :(得分:1)
有两个原因 -
即使是事件驱动的系统通常也需要某种类型的循环来从某种队列中读取事件并将它们分派给处理程序,所以你最终会在windows中结束事件循环,并且可能会很好地扩展它。 / p>
出于动画的目的,您甚至需要为动画的每一帧处理某种形式。您当然可以使用计时器或某种空闲事件来执行此操作,但您最终可能会在某种循环中创建它们,因此使用循环更容易 直接
我见过使用事件处理它的系统,它们有一个框架监听器,用于监听在每个帧开始时调度的事件。它们内部仍然有一个微小的游戏循环,但它只是处理窗口系统事件,并创建帧事件,
答案 10 :(得分:1)
游戏的本质是它们通常是模拟,并不只是基于外部事件而是基于内部过程做出反应。您可以通过重复发生的事件而不是轮询来表示这些内部流程,但它们实际上是等价的:
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编程背景,也许)。