手势控制Snake游戏重启异常:pygame.display.quit后初始化报错
解决Pygame Snake游戏重启时的
display Surface quit错误问题 这个pygame.error: display Surface quit错误的核心原因是你在die()函数里调用了pygame.display.quit(),彻底关闭了Pygame的显示模块,但之后在game()函数里只调用pygame.init()是不够的——pygame.init()不会自动重新初始化已经被显式quit的显示模块,而且你之前的游戏状态(比如蛇的位置、分数)都是全局变量,没有重置,摄像头资源也没正确复用,导致重新进入游戏时出现资源冲突。
下面是具体的解决方案和代码修改:
核心修复步骤
1. 移除pygame.display.quit(),改用状态重置而非销毁窗口
不要在游戏结束时关闭整个Pygame显示,而是保留窗口,重置游戏的核心变量(蛇的位置、方向、分数、苹果位置),这样就能直接重启游戏循环,不用重新初始化显示模块。
2. 重构die()函数,避免销毁关键资源
把pygame.display.quit()和cv2.destroyAllWindows()移除,改为暂停显示分数后,直接重置游戏状态或者回到主菜单。摄像头资源cap应该在游戏结束时释放,然后在重新进入game()时重新打开。
3. 修复enter_name()函数的逻辑错误
你现在的enter_name()函数里,return string在root1.mainloop()之前,这会导致Tk窗口还没运行就直接返回,根本没法输入名字。要把mainloop()改为wait_window(),等待窗口关闭后再返回输入的名字。
4. 新增游戏状态重置函数
专门写一个reset_game()函数来重置蛇的位置、方向、分数和苹果位置,避免全局状态污染。
修改后的关键代码
1. 重构die()函数
def die(screen, score): global conn, cap # 显示结束分数 f = pygame.font.SysFont('Arial', 30) t = f.render('Your score was: ' + str(score), True, (0, 0, 0)) screen.blit(t, (10, 270)) pygame.display.update() pygame.time.wait(2000) # 处理高分逻辑(修复原代码的max/min判断逻辑) r2 = conn.execute("select score from highscore order by score desc limit 10") sc_list = [row[0] for row in r2.fetchall()] # 如果分数够进前10,或者高分榜还没满10个 if (len(sc_list) < 10) or (score > min(sc_list)): n = enter_name() conn.execute("insert into highscore(name,score) values (?,?)", (n, score)) conn.commit() # 释放摄像头资源 cap.release() cv2.destroyAllWindows() # 回到主菜单,不销毁pygame窗口 main_menu()
2. 修复enter_name()函数
def enter_name(): root1 = Tk() root1.geometry('%dx%d+%d+%d' % (700, 500, 640, 100)) root1.title("Enter Your Name") string = StringVar() e = Entry(root1, textvariable=string) e.place(x=250, y=150) e.focus_set() def on_okay(): root1.destroy() b = Button(root1, text='okay', command=on_okay) b.place(x=300, y=200) root1.wait_window() # 等待窗口关闭后再返回输入内容 return string.get()
3. 新增游戏状态重置函数
def reset_game(): # 重置蛇的初始状态 global xs1, ys1, dirs1, score1, applepos1 xs1 = [290, 290, 290] ys1 = [290, 270, 250] dirs1 = 0 # 初始方向(向下) score1 = 0 # 随机生成苹果位置(对齐20px网格) applepos1 = (random.randint(0, 590) // 20 * 20, random.randint(0, 590) // 20 * 20)
4. 修改game()函数支持重启
def game(): global cap, s1, appleimage1, img1, f1, clock1 # 检查Pygame是否已初始化,未初始化则启动 if not pygame.get_init(): pygame.init() # 检查显示窗口是否已创建,未创建则初始化 if not pygame.display.get_init(): s1 = pygame.display.set_mode((600, 600)) pygame.display.set_caption('Snake') SetWindowPos(pygame.display.get_wm_info()['window'], -1, 0, 10, 0, 0, 0x0001) # 初始化游戏资源 appleimage1 = pygame.Surface((20, 20)) appleimage1.fill((255, 0, 0)) img1 = pygame.Surface((20, 20)) img1.fill((0, 255, 0)) f1 = pygame.font.SysFont('Arial', 20) clock1 = pygame.time.Clock() # 重置游戏状态 reset_game() # 重新打开摄像头 cap = cv2.VideoCapture(0) time.sleep(2) while cap.isOpened(): clock1.tick(10) for e in pygame.event.get(): if e.type == QUIT: cap.release() cv2.destroyAllWindows() pygame.quit() sys.exit(0) # 蛇的自碰撞检测 i = len(xs1)-1 while i >= 2: if collide(xs1[0], xs1[i], ys1[0], ys1[i], 20, 20, 20, 20): die(s1, score1) return # 退出当前游戏循环,回到主菜单 i -= 1 # 吃苹果检测 if collide(xs1[0], applepos1[0], ys1[0], applepos1[1], 20, 20, 20, 20): score1 += 1 xs1.append(700) ys1.append(700) applepos1 = (random.randint(0, 590)//20*20, random.randint(0, 590)//20*20) # 边界碰撞检测 if xs1[0] < 0 or xs1[0] > 580 or ys1[0] < 0 or ys1[0] > 580: die(s1, score1) return # 蛇身移动逻辑 i = len(xs1)-1 while i >= 1: xs1[i] = xs1[i-1] ys1[i] = ys1[i-1] i -= 1 # 根据方向移动蛇头 if dirs1 == 0: ys1[0] += 20 elif dirs1 == 1: xs1[0] += 20 elif dirs1 == 2: ys1[0] -= 20 elif dirs1 == 3: xs1[0] -= 20 # 绘制游戏界面 s1.fill((0,0,0)) for i in range(0, len(xs1)): s1.blit(img1, (xs1[i], ys1[i])) s1.blit(appleimage1, applepos1) t1 = f1.render(str(score1), True, (255,255,255)) s1.blit(t1, (10, 10)) pygame.display.update() # 手势识别逻辑(保留原代码) ret, img = cap.read() if not ret: break cv2.rectangle(img, (450,450), (200,200), (0,255,0),0) crop_img = img[200:450, 200:450] grey = cv2.cvtColor(crop_img, cv2.COLOR_BGR2GRAY) value = (35, 35) blurred = cv2.GaussianBlur(grey, value, 0) _, thresh1 = cv2.threshold(blurred, 127, 255, cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) (version, _, _) = cv2.__version__.split('.') if version == '3': image, contours, hierarchy = cv2.findContours(thresh1.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) elif version == '2': contours, hierarchy = cv2.findContours(thresh1.copy(),cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) cnt = max(contours, key = lambda x: cv2.contourArea(x)) x, y, w, h = cv2.boundingRect(cnt) cv2.rectangle(crop_img, (x, y), (x+w, y+h), (0, 0, 255), 0) hull = cv2.convexHull(cnt) drawing = np.zeros(crop_img.shape,np.uint8) cv2.drawContours(drawing, [cnt], 0, (0, 255, 0), 0) cv2.drawContours(drawing, [hull], 0,(0, 0, 255), 0) hull = cv2.convexHull(cnt, returnPoints=False) defects = cv2.convexityDefects(cnt, hull) count_defects = 0 cv2.drawContours(thresh1, contours, -1, (0, 255, 0), 3) if defects is not None: for i in range(defects.shape[0]): s,e,f,d = defects[i,0] start = tuple(cnt[s][0]) end = tuple(cnt[e][0]) far = tuple(cnt[f][0]) a = math.sqrt((end[0] - start[0])**2 + (end[1] - start[1])**2) b = math.sqrt((far[0] - start[0])**2 + (far[1] - start[1])**2) c = math.sqrt((end[0] - far[0])**2 + (end[1] - far[1])**2) angle = math.acos((b**2 + c**2 - a**2)/(2*b*c)) * 57 if angle <= 90: count_defects += 1 cv2.circle(crop_img, far, 1, [0,0,255], -1) cv2.line(crop_img,start, end, [0,255,0], 2) # 手势控制方向 if count_defects == 1 and dirs1 != 0: dirs1 = 2 elif count_defects == 2 and dirs1 != 2: dirs1 = 0 elif count_defects == 3 and dirs1 != 1: dirs1 = 3 elif count_defects == 4 and dirs1 != 3: dirs1 = 1 all_img = np.hstack((drawing, crop_img)) cv2.imshow('Contours', all_img) cv2.imshow('Thresholded', thresh1) cv2.imshow('Gesture', img) cv2.moveWindow('Gesture',700,50) k = cv2.waitKey(10) if k == 27: cap.release() cv2.destroyAllWindows() main_menu() return
额外注意事项
- 确保你的
main_menu()函数中的“开始游戏”按钮直接调用game(),这样用户在主菜单点击就能重新进入游戏,无需重启程序。 - 尽量减少全局变量的使用,后续可以考虑把游戏状态封装成类,让状态管理更清晰。
- 每次进入
game()时,都会检查Pygame和显示窗口的初始化状态,避免重复创建资源导致的冲突。
内容的提问来源于stack exchange,提问作者Aman Mishra
相关产品推荐
相关产品推荐

