【问题标题】:Adding an extra loop causes unresponsiveness添加额外的循环会导致无响应
【发布时间】:2013-10-26 11:24:01
【问题描述】:

我正在使用 Pygame 制作一个简单的游戏,您必须在其中获得(通过将光标移到它们上)将出现在屏幕上的所有圆圈(每秒会出现更多圆圈)。代码很长,所以我做了一个示例代码。这段代码运行良好,Pygame 窗口完全没有反应:

import pygame, random, sys

pygame.init()

window=pygame.display.set_mode((480,360))

end_program=False
while not end_program:
    for event in pygame.event.get():
        if event.type==pygame.QUIT  or  pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
            end_program=True

    pass

pygame.quit()
sys.exit()

但是,在我的游戏中,为了让用户可以选择再次玩,我需要将 end_program 中的所有内容包装在另一个循环中。在显示的示例中,这是break_from_second_loop

import pygame, random, sys

pygame.init()

window=pygame.display.set_mode((480,360))

end_program=False
while not end_program:
    for event in pygame.event.get():
        if event.type==pygame.QUIT  or  pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
            end_program=True

    break_from_second_loop=False

    while not break_from_second_loop:
        pass

pygame.quit()
sys.exit()

现在,如果运行此程序,窗口将变得无响应!任何人都知道为什么像将代码包装在另一个循环中(根本不改变代码)这样​​简单的事情会这样做吗?

【问题讨论】:

  • 尝试创建一个显着简化的示例代码来展示此问题,这将增加您获得建设性回复的机会。
  • @Konrad Rudolph 好主意,完成了。
  • 这将有助于解释代码变得无响应时的位置。 (您可以通过中断查看回溯、在调试器中运行或仅添加一堆 prints 来跟踪代码来实现此目的。)
  • 无论如何,您的简化版本使答案更加明显:while not break_from_second_loop: pass 永远无法完成,而 Python 在它发生时无法做任何其他事情——包括响应事件。

标签: python performance loops pygame


【解决方案1】:

问题在于,如果您没有运行事件循环,游戏将无法响应,或者根本无法执行任何操作。而在另一个循环中,您没有运行事件循环。

这是基于事件循环的编程的普遍问题。你不能做任何需要很长时间的事情,也不能做任何必须跨越多个事件的事情。

因此,您必须将循环分成多个步骤,并且每次通过事件循环只执行一个步骤(或其中几个步骤)。

在这种特殊情况下,它实际上非常简单:只需将 while 更改为 if(并将 has_got_all_circles=False 移到主循环之外),您的逻辑现在每次通过事件循环运行一次.

或者,将其更改为if将其移到for 内,因此现在每个事件只运行一次,而不是每个事件循环迭代一次。

第三种选择是将整个事物分解为一个函数并将其设置为空闲或计时器函数,该函数在事件循环空闲时运行,或每帧一次,或每 20 毫秒一次,或其他任何时间。

很难知道这三个中哪一个适合你的情况,但基本思想都是一样的,所以我只展示第二个:

end_program=False
break_from_second_loop=False
while not end_program:
    for event in pygame.event.get():
        if event.type==pygame.QUIT  or  pygame.key.get_pressed()[pygame.K_ESCAPE]: #If the user either click the "x", or pressed the "esc" key
            end_program=True
        if not break_from_second_loop:
            pass

This blog post 更详细地解释了一般问题——尽管其中大部分内容并不真正适合这个特定问题。

【讨论】:

    【解决方案2】:

    您遇到的问题是您没有将事件循环代码嵌套在执行游戏逻辑的 while 循环中。这是您想要的一般结构:

    while not end_program:
        while not end_game:
            handle_events()
            do_one_frame_of_game_logic()
        offer_another_game()
    

    offer_another_game 也可能需要在自己的循环中运行,并带有自己的事件处理代码。

    确实,您可能希望将要使用的逻辑封装到状态机系统中。你会有像PlayingGameGameOverDoYouWantToPlayAgain 这样的状态,每个状态都会运行一段时间,然后切换到另一个状态。您的主循环将类似于:

    state = StartState()
    while state:
        state.handle_events()
        state.update()
        state.draw()
        state = state.next_state() # most of the time, the state will return itself
    

    【讨论】:

      猜你喜欢
      • 2020-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-01-14
      • 1970-01-01
      • 2020-02-26
      • 2013-01-03
      • 1970-01-01
      相关资源
      最近更新 更多