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

Python turtle贪吃蛇游戏无法结束、墙壁碰撞检测Bug排查

问题原因

代码存在两个核心问题,对应遇到的两个故障:

  • 游戏无法正常终止:turtle.done()的作用是启动Tkinter事件循环、保持窗口不关闭,并非终止游戏逻辑的方法。碰撞触发后没有停止游戏主循环的定时器,tick函数会一直通过turtle.ontimer反复注册执行,蛇的移动、碰撞检测逻辑会持续运行,自然停不下来。
  • 撞墙检测逻辑异常:用坐标完全等于±280作为撞墙判定条件本身就不可靠。蛇初始长度时所有段的坐标都是20的整数倍,移动步长也是20,刚好能命中±280的精确值;但食物生成的随机坐标是任意整数,和20像素的移动网格不对齐,加上Turtle坐标计算的浮点误差,蛇变长后蛇头坐标要么直接越过±280没触发判定,要么身体段位置偏移误触发碰撞,就会出现要么检测不到撞墙、要么没碰墙就弹出GAME OVER的问题。
修复方法
  1. 增加游戏运行状态标记,碰撞触发时修改状态,不再注册下一次主循环定时器,从根源停止游戏逻辑
  2. 把撞墙的精确相等判定改为越界判定,只要蛇头坐标超出边界范围就判定撞墙,不需要精确匹配坐标值
  3. 把食物生成坐标对齐到20像素的移动网格,避免位置偏移带来的判定误差

修正后的完整代码如下:

import turtle
from turtle import Turtle
from random import randint


class Snake:
    def __init__(self, grid_size, initial_x_positions):
        self.segments = []
        self.grid_size = grid_size
        self.create_snake(initial_x_positions)
        self.head = self.segments[0]

    def create_snake(self, initial_x_positions):
        for x in initial_x_positions:
            self.add_segment(x)

    def add_segment(self, position):
        segment = turtle.Turtle("square")
        segment.color("RoyalBlue")
        segment.speed('slowest')
        segment.penup()
        segment.goto(position)
        self.segments.append(segment)

    def extend(self):
        self.add_segment(self.segments[-1].position())

    def move(self):
        for i in range(len(self.segments) - 1, 0, -1):
            x_pos = self.segments[i - 1].xcor()
            y_pos = self.segments[i - 1].ycor()
            self.segments[i].goto(x_pos, y_pos)
        self.head.forward(self.grid_size)

    def up(self):
        if self.head.heading() != 270:
            self.head.setheading(90)

    def down(self):
        if self.head.heading() != 90:
            self.head.setheading(270)

    def left(self):
        if self.head.heading() != 0:
            self.head.setheading(180)

    def right(self):
        if self.head.heading() != 180:
            self.head.setheading(0)


class Food(Turtle):
    def __init__(self):
        super().__init__()
        self.shape('circle')
        self.penup()
        self.color('red3')
        self.shapesize(stretch_len=0.5, stretch_wid=0.5)
        self.speed('fastest')
        self.reset_food_location()

    def reset_food_location(self):
        # 坐标对齐20像素网格,和蛇移动步长匹配
        random_x_pos = randint(-12, 12) * 20
        random_y_pos = randint(-12, 12) * 20
        self.goto(random_x_pos, random_y_pos)


class ScoreBoard(Turtle):
    def __init__(self):
        super().__init__()
        self.score = 0
        self.color('snow')
        self.penup()
        self.goto(0, 250)
        self.hideturtle()
        self.increase_score()

    def increase_score(self):
        self.clear()
        self.write(f"Score: {self.score}", align='center', font=('Courier', 24, 'bold'))
        self.score += 15

    def game_over(self):
        self.goto(0, 0)
        self.write('GAME OVER', align='center', font=('Courier', 24, 'bold'))


def create_screen():
    screen = turtle.Screen()
    screen.tracer(0)
    screen.bgcolor("black")
    screen.title("Snake Game")
    screen.setup(width=600, height=600)
    return screen


def control_snake_movement(screen, snake):
    screen.listen()
    screen.onkey(key="Up", fun=snake.up)
    screen.onkey(key="Down", fun=snake.down)
    screen.onkey(key="Left", fun=snake.left)
    screen.onkey(key="Right", fun=snake.right)


def main():
    initial_x_position = [(0, 0), (-20, 0), (-40, 0)]
    grid_size = 20
    frame_delay_ms = 80
    # 新增游戏运行状态标记
    game_is_running = True

    screen = create_screen()
    snake = Snake(grid_size, initial_x_position)
    control_snake_movement(screen, snake)
    food = Food()
    scoreboard = ScoreBoard()

    def tick():
        nonlocal game_is_running
        snake.move()
        screen.update()

        # 食物碰撞检测
        if snake.head.distance(food) < 20:
            food.reset_food_location()
            snake.extend()
            scoreboard.increase_score()

        # 墙壁碰撞检测 改为越界判定,不用精确匹配坐标
        if abs(snake.head.xcor()) > 280 or abs(snake.head.ycor()) > 280:
            scoreboard.game_over()
            game_is_running = False

        # 尾巴碰撞检测
        for segment in snake.segments[1:]:
            if snake.head.distance(segment) < 10:
                scoreboard.game_over()
                game_is_running = False
                break
        
        # 只有游戏运行中才注册下一次循环
        if game_is_running:
            turtle.ontimer(tick, frame_delay_ms)

    tick()
    screen.exitonclick()
    turtle.done()


if __name__ == "__main__":
    main()

修改后碰撞触发时会直接停止主循环,蛇不会再移动,撞墙判定也不会再出现漏判、误判的问题。


内容的提问来源于stack exchange,提问作者漣侑稀

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 23:21:23