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

Python:海龟图形中浮点取整优化集合碰撞检测方案问询

解决Turtle库贪吃蛇游戏的碰撞检测与食物重叠问题

问题背景

开发基于Turtle库的贪吃蛇时,遇到两个核心问题:

  • 蛇身自撞检测因浮点数精度失效:Turtle的.position()返回10位小数的浮点数,即使位置相同数值也可能存在微小差异,直接转整数会有精度偏差,用保留两位小数的方式仍存在隐患。
  • 食物生成时偶尔与蛇身重叠:现有食物刷新逻辑中,坐标格式与蛇身坐标不统一,导致重叠判断失效。

优化方案

1. 统一整数坐标系统

由于蛇每次固定移动20单位,所有有效位置必然是20的整数倍,直接将坐标转为整数即可彻底规避浮点数精度问题,无需保留小数。

2. 简化蛇身坐标更新逻辑

不再从Turtle对象读取位置,而是通过逻辑计算直接更新坐标列表,既提升效率又避免精度误差。

3. 修复食物生成的坐标匹配

食物生成时直接生成20的整数倍坐标,与蛇身坐标格式统一,确保重叠判断准确。

修改后的代码

gamesetup.py

import turtle as t

class FullSnake:
    def __init__(self) -> None:
        self.snake_part_list = []
        # 初始坐标全部使用整数
        self.snake_coord_list = [(-40, 0), (-60, 0), (-80, 0)]
        self.moment_prior_coord_list = [(-40, 0), (-60, 0), (-80, 0)]
        for nums in range(3):
            new_segment = t.Turtle("circle")
            new_segment.penup()
            new_segment.color("white")
            self.snake_part_list.append(new_segment)
            new_segment.goto(self.snake_coord_list[nums])
        self.snake_head = self.snake_part_list[0]
        self.headingverification = 0

    def add_segment(self):
        """在蛇尾添加新段"""
        new_segment = t.Turtle("circle")
        new_segment.penup()
        new_segment.color("white")
        self.snake_part_list.append(new_segment)
        current_final_seg_pos = self.moment_prior_coord_list[-1]
        new_segment.goto(current_final_seg_pos)
        self.snake_coord_list.append(current_final_seg_pos)

    def right(self):
        if self.headingverification != 180:
            self.snake_head.setheading(0)
    def up(self):
        if self.headingverification != 270:
            self.snake_head.setheading(90)
    def left(self):
        if self.headingverification != 0:
            self.snake_head.setheading(180)
    def down(self):
        if self.headingverification != 90:
            self.snake_head.setheading(270)

    def move_snake(self):
        """移动蛇:头部前进,后续段跟随前一段位置"""
        self.moment_prior_coord_list = self.snake_coord_list.copy()
        # 先更新身体坐标与位置
        for seg_num in range(len(self.snake_part_list)-1, 0, -1):
            self.snake_coord_list[seg_num] = self.snake_coord_list[seg_num-1]
            self.snake_part_list[seg_num].goto(self.snake_coord_list[seg_num])
        # 移动头部并更新坐标为整数
        self.snake_head.forward(20)
        head_x = int(self.snake_head.xcor())
        head_y = int(self.snake_head.ycor())
        self.snake_coord_list[0] = (head_x, head_y)

food.py

from turtle import Turtle
import random

screensize = (340, 340)

class Food(Turtle):
    def __init__(self) -> None:
        super().__init__()
        self.shape("square")
        self.penup()
        self.color("red")
        self.speed("fastest")
        self.refresh([(20,0),(0,0), (-20,0)])

    def refresh(self, positionlist):
        # 直接生成20的整数倍坐标,与蛇身格式统一
        while True:
            randomx = random.randint(-7, 7) * 20
            randomy = random.randint(-7, 7) * 20
            newloc = (randomx, randomy)
            if newloc not in positionlist:
                break
        self.goto(randomx, randomy)
        print(f"{randomx} and {randomy}")

main.py

import turtle as t
from gamesetup import FullSnake
import time
import food
import score_board as sb

screen = t.Screen()
screen.setup(food.screensize[0],food.screensize[1])
screen.bgcolor("Black")
screen.title("My Snake Game")
screen.tracer(0)

snakefood = food.Food()
snake = FullSnake()
scoreboard = sb.ScoreBoard()

screen.listen()
screen.onkey(snake.up,"Up")
screen.onkey(snake.down,"Down")
screen.onkey(snake.right,"Right")
screen.onkey(snake.left,"Left")

def snakemovefct(): 
    snake.move_snake()      
    screen.update()

    if snake.snake_head.distance(snakefood) <5:
        snake.add_segment()
        snakefood.refresh(snake.snake_coord_list)
        scoreboard.score_event()
        screen.update()
        time.sleep(0.1)

    # 边界碰撞检测,使用整数坐标判断
    head_x, head_y = snake.snake_coord_list[0]
    if head_x > 150 or head_x < -150 or head_y > 150 or head_y < -150:
        scoreboard.game_over()
        return

    # 蛇身自撞检测:直接检查头部坐标是否在身体坐标列表中
    if snake.snake_coord_list[0] in snake.snake_coord_list[1:]:
        scoreboard.game_over()
        return
    snake.headingverification = snake.snake_head.heading()
    screen.ontimer(snakemovefct,150)

screen.update()
screen.ontimer(snakemovefct,150)
screen.mainloop()

scoreboard.py

代码无需修改,保持原逻辑即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 15:45:28