为什么“主要”是“主”?开发游戏需要游戏循环吗?

发布于 2024-08-27 15:39:59 字数 101 浏览 4 评论 0原文

我发现大多数游戏开发都需要主游戏循环,但我不知道为什么有必要。难道我们不能实现一个事件监听器并响应每个用户操作吗?当事件发生时可以播放动画(等)。

主游戏循环的目的是什么?

I find that most game development requires a main game loop, but I don't know why it's necessary. Couldn't we implement an event listener and respond to every user action? Animations (etc.) could then be played when a event occurs.

What is the purpose of a main game loop?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(12

留一抹残留的笑 2024-09-03 15:40:00

制定严格且确定性的游戏循环有很多好处,例如更好的故障重现。另一个是你的游戏更有可能成为一款速通游戏(如果你将其设计为具有竞争力)。许多游戏玩家依赖他们的肌肉记忆,因此确定性输出对于策略非常重要。使用 TAS(工具辅助 Speedrun)也变得更容易,因为当给定相同的输入时,程序会输出相同的内容。

There are many benefits of making a strict and deterministic game loop such as better glitch recreation. Another one is your game is more likely to become a speedrun game (if you designed it to be competitive). Many gamers rely on their muscle memory so deterministic output is important for strategy. Also using TAS (Tool Asisted Speedrun) becomes easier since the program outputs the same thing when given the same inputs.

亢潮 2024-09-03 15:39:59

“需要一个循环,因为否则调用事件侦听器”的论点是站不住脚的。诚然,在任何主流操作系统上,确实有这样的循环,并且事件侦听器确实以这种方式工作,但完全有可能制作一个无需任何类型的循环即可工作的中断驱动系统。

但你仍然不想以这种方式构建游戏。

循环成为最有吸引力的解决方案的原因在于,循环成为实时编程中所谓的“循环执行程序”。这个想法是,您可以使各种系统活动的相对执行率彼此确定。循环的总体速率可以由计时器控制,并且该计时器最终可能是一个中断,但是对于现代操作系统,您可能会看到该中断的证据作为等待信号量(或其他一些同步机制)的代码,如下所示“主循环”的一部分。

那么为什么你想要确定性行为呢?考虑用户输入和坏人人工智能的相对处理速率。如果您将所有内容放入纯粹基于事件的系统中,则无法保证 AI 不会比您的用户获得更多的 CPU 时间,反之亦然,除非您对线程优先级有一定的控制,即使这样,您容易难以保持时间一致。

然而,将所有内容放在一个循环中,您就可以保证您的人工智能时间线将按照与用户时间相关的固定关系进行。这是通过从循环中发出调用来给 AI 一个时间片来决定要做什么、调用用户输入例程、轮询输入设备以了解用户想要的行为方式来完成的,以及呼叫进行渲染。

通过这样的循环,您必须注意处理每次传递所花费的时间不会比实际实时花费的时间多。如果您尝试以 100Hz 循环循环,则所有循环处理最好在 10 毫秒内完成,否则您的系统将会变得不稳定。在实时编程中,这称为超出时间范围。一个好的系统可以让您监控距离超限的程度,然后您可以根据需要减轻处理负载。

The argument that you "need a loop because otherwise what calls the event listener" does not hold water. Admittedly on any mainstream OS, you do indeed have such a loop, and event listeners do work that way, but it is entirely possible to make an interrupt driven system that works without any loops of any kind.

But you still would not want to structure a game that way.

The thing that makes a loop the most appealing solution is that your loop becomes what in real-time programming is referred to as a 'cyclic executive'. The idea is that you can make the relative execution rates of the various system activities deterministic with respect to one another. The overall rate of the loop may be controlled by a timer, and that timer may ultimately be an interrupt, but with modern OS's, you will likely see evidence of that interrupt as code that waits for a semaphore (or some other synchronization mechanism) as part of your "main loop".

So why do you want deterministic behavior? Consider the relative rates of processing of your user's inputs and the baddies AIs. If you put everything into a purely event based system, there's no guarantee that the AIs won't get more CPU time than your user, or the other way round, unless you have some control over thread priorities, and even then, you're apt to have difficulty keeping timing consistent.

Put everything in a loop, however, and you guarantee that your AIs time-lines are going to proceed in fixed relationship with respect to your user's time. This is accomplished by making a call out from your loop to give the AIs a timeslice in which to decide what to do, a call out to your user input routines, to poll the input devices to find out how your user wants to behave, and call out to do your rendering.

With such a loop, you have to watch that you are not taking more time processing each pass than actually goes by in real time. If you're trying to cycle your loop at 100Hz, all your loop's processing had better finish up in under 10msec, otherwise your system is going to get jerky. In real-time programming, it's called overrunning your time frame. A good system will let you monitor how close you are to overrunning, and you can then mitigate the processing load however you see fit.

岁月蹉跎了容颜 2024-09-03 15:39:59

事件侦听器还依赖于某些调用循环,无论您是否看到它。还有谁会打电话给听众?

构建显式的游戏循环可以让您对正在发生的事情进行绝对控制,因此您不会依赖于某些工具包/事件处理库在其事件循环中所做的任何事情。

An event listener is also dependent on some invocation loop whether you see it or not. Who else is going to call the listener?

Building an explicit game loop gives you absolute control on what's going on so you won't be dependent on whatever some toolkit/event handling library does in its event loop.

夜清冷一曲。 2024-09-03 15:39:59

游戏循环(高度简化如下)

initialise
do
     input
     update
     render
loop
clean up

这将在游戏绘制的每一帧发生。因此,对于以 60fps 运行的游戏,上述操作每秒执行六十次。

这意味着游戏运行平稳,游戏保持同步,并且每个周期的更新/抽奖发生得足够频繁。动画只是眼睛的把戏,物体在位置之间移动,但如果播放得足够快,它们看起来就像在这些位置之间移动。

如果您只更新用户输入,则游戏只会在用户提供输入时做出反应。其他游戏组件(例如 AI 游戏对象)不会自行做出反应。因此,循环是更新游戏最简单、最好的方法。

A game loop (highly simplified is as follows)

initialise
do
     input
     update
     render
loop
clean up

This will happen every frame the game is drawn. So for games that run at 60fps the above is performed sixty times every second.

This means the game runs smoothly, the game stays in sync and the updates/draws per cycle happen frequently enough. Animation is simply a trick of the eye, objects move between locations but when played quickly enough they appear to be travelling between these locations.

If you were to only update on user input, the game would only react when the user was providing input. Other game components such as A.I game objects would not react on their own. A loop is therefore the easiest and best way of updating a game.

霞映澄塘 2024-09-03 15:39:59

并非所有类型的游戏都需要专用的主游戏循环。

由于频繁的对象更新和游戏输入的精确性,动作游戏需要这样的循环。

另一方面,我实现了一个扫雷游戏,并使用了 window
通知的消息。

It's not true that all kind of games require a dedicated main game loop.

Action games need such a loop due to frequent object updates and game input precision.

On the other hand, I implemented a minesweeper game and I used window
messages for the notifications.

薄荷梦 2024-09-03 15:39:59

这是因为当前的操作系统并不完全基于事件。尽管事物通常被表示为事件,但您仍然需要创建一个循环来等待下一个事件并无限期地处理它(例如 Windows 事件循环)。 Unix 信号可能是您在操作系统级别上获得的最接近事件的东西,但对于此类事情来说,它们的效率不够高。

It's because current operating systems aren't fully event based. Even though things are often represented as events, you'll still have to create a loop where you wait for the next event and process it indefinitely (as an example the Windows event loop). Unix signals are probably the closest thing you get to events on an OS level, but they're not really efficient enough for things like this.

时光暖心i 2024-09-03 15:39:59

实际上,正如其他人指出的那样,需要一个循环。

然而,你的想法在理论上是合理的。您不需要循环。您需要基于事件的操作。

简单地说,您可以将 CPU 概念化为具有多个定时器;

  • 其中一个在 60Hz 的上升沿触发并调用位块传送代码。
  • 另一种可能是以 60kHz 的频率滴答,并将游戏世界中对象的最新更新渲染到位块缓冲区。
  • 另一个可能以 10kHz 滴答并接收用户的输入。 (相当高的分辨率,哈哈)
  • 另一个可能是游戏“心跳”,滴答频率为 60MHz;人工智能和物理可能会在心跳时间运行。

当然这些定时器是可以调整的。

实际上,会发生的事情是你会(有点被省略)像这样:

void int_handler1();
//...
int main() 
{ 
  //install interrupt handlers
  //configure settings
  while(1);
}

In practical terms, as other people have indicated, a loop is needed.

However, your idea is theoretically sound. You don't need a loop. You need event-based operations.

At a simple level, you can conceptualize the CPU to have a several timers;

  • one fires on the rising edge of 60Hz and calls the blitting code.
  • Another might be ticking at 60kHz and be rendering the latest updates of the objects in the game world to the blitter buffer.
  • Another might be ticking at 10kHz and be taking input from the user. (pretty high resolution, lol)
  • Another might be the game 'heartbeat' and ticks at 60MHz; AI and physics might operate at heartbeat time.

Of course these timers can be tuned.

Practically, what would be happening is your would be (somewhat elided) like this:

void int_handler1();
//...
int main() 
{ 
  //install interrupt handlers
  //configure settings
  while(1);
}
夏日落 2024-09-03 15:39:59

游戏的本质是它们通常是模拟,不仅根据外部事件做出反应,还根据内部过程做出反应。您可以通过重复事件而不是轮询来表示这些内部流程,但它们实际上是等效的:

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 实现基于事件的方法而不是更传统的循环。虽然这在很多时候都工作得很好,但有时它会导致性能问题或由时钟分辨率、垂直同步等引起的不可预测的行为。循环更可预测且更容易理解(除非您来自专门的 Web 编程背景,也许)。

The nature of games is that they're typically simulations, and are not just reacting based on external events but on internal processes as well. You could represent these internal processes by recurring events instead of polling, but they're practically equivalent:

schedule(updateEvent, 33ms)

function updateEvent:
    for monster in game:
        monster.update()
    render()

vs:

while 1:
    for monster in game:
        monster.update()
    wait(33ms)
    render()

Interestingly, pyglet implements the event-based method instead of the more traditional loop. And while this works well a lot of the time, sometimes it causes performance problems or unpredictable behaviour caused by the clock resolution, vsync, etc. The loop is more predictable and easier to understand (unless you come from an exclusively web programming background, perhaps).

浮生面具三千个 2024-09-03 15:39:59

任何可以无限期地停留在那里并响应用户输入的程序都需要某种循环。否则它将到达程序末尾并退出。

Any program that can just sit there indefinitely and respond to user's input needs some kind of loop. Otherwise it will just reach the end of program and will exit.

一直在等你来 2024-09-03 15:39:59

主循环调用事件监听器。如果您足够幸运拥有事件驱动的操作系统或窗口管理器,则事件循环就驻留在其中。否则,您需要编写一个主循环来调解基于 I/O、pollselect 的系统调用接口与传统的系统调用接口之间的“阻抗不匹配”。事件驱动的应用程序。

PS既然你用函数式编程标记了你的问题,你可能想看看函数式响应式编程 ,它在将高级抽象与低级基于事件的实现连接起来方面做得很好。

The main loop calls the event listener. If you are lucky enough to have an event-driven operating system or window manager, the event loop resides there. Otherwise, you write a main loop to mediate the "impedance mismatch" between an system-call interfaces that is based on I/O, poll, or select, and a traditional event-driven application.

P.S. Since you tagged your question with functional-programming, you might want to check out Functional Reactive Programming, which does a great job connecting high-level abstractions to low-level, event-based implementations.

佞臣 2024-09-03 15:39:59

游戏需要实时运行,因此在运行时效果最佳连续在一个 CPU/核心上运行。当队列中没有事件时,事件驱动的应用程序通常会将 CPU 交给另一个线程。 CPU 切换回您的进程之前可能需要等待相当长的时间。在游戏中,这意味着短暂的停顿和紧张的动画。

A game needs to run in real-time, so it works best if it is running on one CPU/core continuously. An event-driven application will typically yield the CPU to another thread when there is no event in the queue. There may be a considerable wait before the CPU switches back to your process. In a game, this would mean brief stalls and jittery animation.

私藏温柔 2024-09-03 15:39:59

有两个原因 -

即使事件驱动的系统通常也需要某种类型的循环,从某种类型的队列中读取事件并将它们分派给处理程序,因此您最终会在 Windows 中得到一个事件循环,并且可能会很好地扩展它。

出于动画的目的,您需要对动画的每一帧进行某种处理。您当然可以使用计时器或某种空闲事件来完成此操作,但无论如何您最终可能会在某种循环中创建它们,因此使用循环更容易
直接地。

我见过确实使用事件处理所有问题的系统,它们有一个帧侦听器,用于侦听在每个帧开始时调度的事件。他们内部仍然有一个很小的游戏循环,但它除了处理窗口系统事件和创建帧事件外,几乎没有什么作用,

Two reasons -

Even event driven systems usually need a loop of some kind that reads events from a queue of some kind and dispatches them to a handler so you end up with an event loop in windows for example anyway and might was well extend it.

For the purposes of animation you'd need to handle some kind of even for every frame of the animation. You could certainly do this with a timer or some kind of idle event, but you'd probably end up creating those in some kind of loop anyway so it's just easier to use the loop
directly.

I've seen systems that do handle it all using events, they have a frame listener that listens to an event dispatched at the start of each frame. They still have a tiny game loop internally but it does little more than handle windowing system events, and create frame events,

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文