You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Pygame游戏随机无响应问题排查求助

Pygame随机无响应问题排查(菜单状态处理相关)

我是Pygame开发新手,遇到难以稳定复现的随机无响应问题,怀疑问题出在菜单状态处理环节。该问题最常出现在按ESC键进入暂停菜单时(过去快速切换菜单也会触发,添加冷却机制后此场景已解决)。游戏99%时间运行正常,但偶尔会窗口冻结无响应,往往需半小时反复尝试才能复现一次。

相关代码片段

游戏循环

while settings.RUNNING:

    settings.EVENTS = pygame.event.get()
    
    if (settings.MUSIC_MUTED):
        pygame.mixer.music.set_volume(0)
    else:
        pygame.mixer.music.set_volume(int(settings.MUSIC_VOLUME) / 100)


    if not (settings.GAME_PAUSED):

        for event in settings.EVENTS:
            if event.type == pygame.QUIT:
                settings.RUNNING = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    settings.MENU_STATE = 'pause'
                    pause_start = pygame.time.get_ticks()
                    settings.MENU_CD = pygame.time.get_ticks()
                    settings.GAME_PAUSED = True

        if settings.FINISHED:
            settings.FINISHED = False
            level = Level(settings)

        pygame.mixer.music.unpause()
        level.draw()


    elif (settings.GAME_PAUSED):
      #Some other code

    pygame.display.update()
    GAME_CLOCK.tick(60)

pygame.quit()
sys.exit()

暂停事件处理

for event in settings.EVENTS:
            if event.type == pygame.QUIT:
                settings.RUNNING = False
            if settings.MENU_STATE == "main":
                if event.type == pygame_gui.UI_TEXT_ENTRY_CHANGED and event.ui_object_id == "#name_input_field" and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
                    settings.PLAYER_NAME = event.text
                NAME_INPUT.process_events(event)
            if settings.MENU_STATE == "keybinds" and waiting_input:
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        if waiting_for == "left":
                            left_button.image = settings.MENU_ASSETS["buttons"]["button"]
                        if waiting_for == "right":
                            right_button.image = settings.MENU_ASSETS["buttons"]["button"]
                        if waiting_for == "jump":
                            jump_button.image = settings.MENU_ASSETS["buttons"]["button"]
                        waiting_for = ""
                        waiting_input = False
                    elif event.key not in [settings.KEYBINDS['left'], settings.KEYBINDS['right'], settings.KEYBINDS['jump']]:
                        if waiting_for == "left":
                            save_settings({
                                'left' : event.key,
                                'right': settings.KEYBINDS['right'],
                                'jump': settings.KEYBINDS['jump']
                            })
                            left_button.image = settings.MENU_ASSETS["buttons"]["button"]
                        elif waiting_for == "right":
                            save_settings({
                                'left' : settings.KEYBINDS['left'],
                                'right': event.key,
                                'jump': settings.KEYBINDS['jump']
                            })
                            right_button.image = settings.MENU_ASSETS["buttons"]["button"]
                        elif waiting_for == "jump":
                            save_settings({
                                'left' : settings.KEYBINDS['left'],
                                'right': settings.KEYBINDS['right'],
                                'jump': event.key
                            })
                            jump_button.image = settings.MENU_ASSETS["buttons"]["button"]

                        set_keybinds(settings)
                        waiting_input = False
                        waiting_for = ""

暂停菜单代码

if (settings.MENU_STATE == 'pause'):
            print("pause menu", settings.GAME_PAUSED, settings.RUNNING)
            pygame.mixer.music.pause()
            title_image = pygame.image.load('./assets/menu/title/title.png').convert_alpha()
            SCREEN.blit(title_image, ((settings.SCREEN_WIDTH/2 - title_image.get_width()/2),(settings.SCREEN_HEIGHT/4 - title_image.get_height() * 3 / 4)))

            if resume_button.draw(SCREEN) and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
                settings.GAME_PAUSED = False
                level.pause_time += (pygame.time.get_ticks() - pause_start)
                pause_start = 0
                continue

            if settings_button.draw(SCREEN) and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
                settings.PREVIOUS_MENU = "pause"
                settings.MENU_STATE = "settings"
                settings.MENU_CD = pygame.time.get_ticks()
                continue

            if mainmenu_button.draw(SCREEN) and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
                settings.MENU_STATE = "main"
                settings.MENU_CD = pygame.time.get_ticks()
                continue

补充信息:Python版本为3.9.13

可能的问题点及修复建议

1. 重复加载图片导致阻塞

暂停菜单中每次进入pause状态都会调用pygame.image.load加载图片,磁盘IO是阻塞操作,偶尔会因系统资源调度引发卡顿甚至冻结。解决方法:将图片加载移至初始化阶段,避免重复IO:

# 初始化阶段(比如游戏启动时)提前加载
title_image = pygame.image.load('./assets/menu/title/title.png').convert_alpha()

# 暂停菜单中直接使用已加载的图片
if (settings.MENU_STATE == 'pause'):
    print("pause menu", settings.GAME_PAUSED, settings.RUNNING)
    pygame.mixer.music.pause()
    SCREEN.blit(title_image, ((settings.SCREEN_WIDTH/2 - title_image.get_width()/2),(settings.SCREEN_HEIGHT/4 - title_image.get_height() * 3 / 4)))
    # 后续按钮逻辑...

2. continue语句跳过关键循环步骤

暂停菜单按钮逻辑中使用continue,会直接回到游戏循环开头,跳过当前周期的pygame.display.update(),导致画面无法更新,看起来像窗口冻结。解决方法:移除continue,用状态变量切换代替:

if resume_button.draw(SCREEN) and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
    settings.GAME_PAUSED = False
    level.pause_time += (pygame.time.get_ticks() - pause_start)
    pause_start = 0
    # 移除continue,让循环正常执行到display.update

if settings_button.draw(SCREEN) and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
    settings.PREVIOUS_MENU = "pause"
    settings.MENU_STATE = "settings"
    settings.MENU_CD = pygame.time.get_ticks()
    # 移除continue

# 同理处理mainmenu_button

3. 暂停状态下事件处理不完整

当前GAME_PAUSED分支中,暂停菜单状态未处理QUIT事件和ESC键的二次响应,可能导致事件队列堆积或无法正常退出。解决方法:在暂停菜单的事件处理中补充关键事件逻辑:

elif (settings.GAME_PAUSED):
    for event in settings.EVENTS:
        if event.type == pygame.QUIT:
            settings.RUNNING = False
        # 处理暂停状态下的ESC键(比如直接恢复游戏)
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_ESCAPE and (pygame.time.get_ticks() - settings.MENU_CD) >= 175:
                settings.GAME_PAUSED = False
                level.pause_time += (pygame.time.get_ticks() - pause_start)
                pause_start = 0
    # 暂停菜单绘制逻辑...

4. 设置保存的阻塞风险

检查save_settings函数,如果涉及同步文件IO,长时间阻塞主线程会导致游戏无响应。解决方法:将设置保存改为异步操作,或确保IO操作足够轻量化,避免阻塞游戏循环。

5. 全局变量的竞态风险

如果存在异步操作修改settings模块的全局变量(如RUNNING、GAME_PAUSED),可能引发竞态条件导致死锁。解决方法:确保所有全局变量的修改都在主线程中进行,或添加线程同步机制。


内容的提问来源于stack exchange,提问作者Anze

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 16:55:25