Pygame蛇形游戏无用户输入时主循环阻塞问题求助
蛇形游戏主循环冻结问题排查与修复
问题描述
我用Python+Pygame编写了一款蛇形游戏原型,在手机Pydroid和在线解释器中测试时,游戏仅运行2次迭代后就冻结,只有触摸屏幕(触发事件)时才会恢复运行。
原代码
主游戏文件
import sys import pygame from pygame.locals import * from libs.Touch import Touch def sortPairs(*pairs): result = {} for pair in pairs: result[pair[0]] = pair[1] result[pair[1]] = pair[0] return result DIR = { 'LEFT': (-1, +0), 'RIGHT': (+1, +0), 'UP': (+0, -1), 'DOWN': (+0, +1) } DIR['INVERSE'] = sortPairs( (DIR['LEFT'], DIR['RIGHT']), (DIR['UP'], DIR['DOWN'])) class Snake: def __init__(self,x, y): self.dir = DIR['RIGHT'] self.nodes = [[x, y]] def move(self): for i in range(len(self.nodes) - 1, 0, -1): self.nodes[i][0] = self.nodes[i - 1][0] self.nodes[i][1] = self.nodes[i - 1][1] self.nodes[0][0] += self.dir[0] self.nodes[0][1] += self.dir[1] def turn(self, turn): dir = DIR[turn] if len(self.nodes) == 1 or dir != DIR['INVERSE'][turn]: self.dir = dir def grow(self): self.nodes.appendLeft([ self.nodes[0][0] + self.dir[0], self.nodes[0][1] + self.dir[1]]) def draw(self, surface,x, y, node_size): rect = pygame.Rect( 0, 0, node_size, node_size) for node in self.nodes: rect.left = x + node[0] * node_size rect.top = y + node[1] * node_size rect.right = rect.left + node_size rect.bottom = rect.top + node_size pygame.draw.rect( surface, pygame.Color("#FF0000"), rect) class Game: def __init__(self, size, tick): self.size = size self.tick = tick self.snake = Snake(0, 0) def draw(self, surface): surface.fill(pygame.Color('#161616')) cell_size = min( surface.get_width(), surface.get_height()) / self.size bounds_stroke = int(cell_size / 10) bounds = pygame.Rect( surface.get_width() / 2, surface.get_height() / 2, cell_size * self.size - bounds_stroke, cell_size * self.size - bounds_stroke) bounds.center = (bounds.x, bounds.y) self.snake.draw( surface, int(bounds.left), int(bounds.top), int(cell_size)) pygame.draw.rect( surface, pygame.Color('#6f91b3'), bounds, bounds_stroke) def update(self): self.snake.move() def handle_event(self, event): if event.type == MOUSEBUTTONDOWN: pygame.mouse.get_rel() if event.type == MOUSEBUTTONUP: swipe = Touch.reg_swipe(*pygame.mouse.get_rel()) if swipe != -1: self.snake.turn(swipe) if event.type == QUIT: pygame.quit() def main(): game = Game(30, 100) pygame.init() # Resolution is ignored on Android surface = pygame.display.set_mode((640, 480)) clock = pygame.time.Clock() delta = 0 while True: for ev in pygame.event.get(): game.handle_event(ev) delta += clock.tick(60) if delta >= game.tick: game.update() delta = 0 game.draw(surface) pygame.display.flip() if __name__ == '__main__': main()
./libs/Touch.py
class Touch: max_touch_dist = 15 min_swipe_dist = 50 @staticmethod def reg_swipe(rel_x, rel_y): min = Touch.min_swipe_dist if abs(rel_y) < min: if rel_x <= -min: return 'LEFT' elif rel_x >= min: return 'RIGHT' if rel_y <= -min: return 'UP' elif rel_y >= min: return 'DOWN' return -1
问题根源
核心问题是主循环的逻辑缩进错误:
所有游戏核心逻辑(计时累加、状态更新、画面绘制、屏幕刷新)都被嵌套在了for ev in pygame.event.get()的事件循环内部。这意味着只有当有事件触发时(比如触摸、点击、窗口事件),这些逻辑才会执行;当没有事件发生时,游戏就会停止所有更新和绘制,表现为冻结状态。
Pygame的主循环正确结构应该是:
- 处理所有事件(在事件循环内)
- 执行游戏状态更新、计时、绘制(在事件循环外,主循环的每一轮迭代都要执行)
修复方案
1. 修正主循环缩进
将计时、更新、绘制逻辑移出事件循环,放到主循环的顶层:
def main(): game = Game(30, 100) pygame.init() # Resolution is ignored on Android surface = pygame.display.set_mode((640, 480)) clock = pygame.time.Clock() delta = 0 while True: # 仅在事件循环内处理事件 for ev in pygame.event.get(): game.handle_event(ev) # 核心逻辑移到主循环顶层,每帧都执行 delta += clock.tick(60) if delta >= game.tick: game.update() delta = 0 game.draw(surface) pygame.display.flip()
2. 修复Snake类的grow方法
Python列表没有appendLeft方法,需要改用insert(0, ...)实现头部添加节点:
def grow(self): self.nodes.insert(0, [ self.nodes[0][0] + self.dir[0], self.nodes[0][1] + self.dir[1]])
3. 完善退出逻辑
handle_event方法中,收到QUIT事件时仅调用pygame.quit()无法确保程序完全退出,需补充sys.exit():
if event.type == QUIT: pygame.quit() sys.exit()
内容的提问来源于stack exchange,提问作者K. Russell Smith
相关产品推荐
相关产品推荐

