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

手势控制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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:54:57