在PyGame中处理定时事件的最佳方法

2 投票
2 回答
2399 浏览
提问于 2025-04-15 23:09

我正在制作一个类似俄罗斯方块的游戏,想知道每隔N秒让方块下落的逻辑该怎么处理。我最开始用的方式很简单:

pygame.time.set_timer(USEREVENT+1, 300)

这样做的话,每300毫秒就会在事件队列中添加一个PyGame事件。在主循环中,我可以检测这个事件,并在事件发生时让方块下落。不过,这种方法有个问题,就是玩家可以通过键盘输入快速生成很多事件,导致事件队列被淹没。我发现玩家只要不停地按上箭头键,就能让方块一直停在同一个高度,因为这个键可以让方块翻转。即使检测定时器事件的代码在检测键盘事件之前执行,似乎也没什么关系。键盘事件总是优先级更高。你知道怎么防止这种情况发生吗?

如果这是pygame.time的一个不可避免的特性,那有没有更好的方法来处理定时事件呢?

  • 在每次循环迭代中检查经过的时间,如果超过某个值就让方块下落?
  • 使用sched或者类似的东西在一个单独的线程中运行,并通过某种共享资源进行信号传递?

有什么想法吗?

2 个回答

0

对Dietrich Epp的回答做个小更正——redraw screen其实应该是在两个连续的游戏状态之间进行插值或外推。这是为了给玩家提供更流畅的体验。

在插值的情况下,插值因子是用更新后“剩下”的时间(因为它小于frame time)除以帧时间,这样可以得到一个在[0..1]范围内的结果。

这也被称为标准游戏循环

3

下面是一个典型的游戏循环是怎么运作的:

repeat forever:
    read all pending events
    if current time > last frame + frame time:
        last frame = last frame + frame time
        update game
    redraw screen

如果有一个定时器本来应该每300毫秒触发一次,但在用户频繁操作游戏时却不工作,那可能是定时器坏了。不过,如果你在每次用户操作时都重绘屏幕,而不是等处理完所有待处理的操作再重绘,那问题就出在你的代码上了。

这里有一个游戏循环的坏例子:

repeat forever:
    read the next pending event
    if current time > last frame + frame time:
        last frame = last frame + frame time
        update game
    redraw screen

这个循环出问题了,因为它重绘屏幕的频率太高了。如果玩家同时按下两个键,系统会把它们分开到不同的帧,这样做其实没必要。

上面的循环假设你希望尽可能频繁地更新屏幕。这种情况很常见,但你也可能希望更新屏幕的频率低一些。只要在重绘屏幕之前处理完所有待处理的事件,你的事件处理方式就没问题。

附注:绝对不要用多线程来解决这个问题。那样会造成很大的混乱。

撰写回答