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

Python Turtle Tron游戏 记录移动坐标到集合实现碰撞检测

问题根因

集合仅保存初始位置、不追加移动坐标的问题,基本是踩了这三个坑:

  1. 作用域错误:在move_player函数内部重新定义了同名的轨迹集合变量,实际操作的是函数局部的空集合,没有修改外层初始化的正式轨迹集合。
  2. 数据类型错误:直接存储turtle.pos()返回的浮点型Vec2D对象,一方面turtle内部浮点计算存在微小精度误差,看似相同的坐标实际值差了万分之一个像素,导致集合匹配失败;另一方面未做类型转换时,每次生成的新Vec2D对象哈希值不统一,集合判定逻辑失效。
  3. 执行时机错误:把轨迹追加逻辑写在了移动分支外、或者碰撞判定逻辑之后,移动操作完成后根本没触发坐标追加代码。
可直接运行的正确实现方案

核心逻辑是固定整数步长移动、坐标转整数元组存集合、移动完成后立刻追加轨迹、双向判定碰撞。

import turtle

# 初始化游戏窗口
screen = turtle.Screen()
screen.setup(width=600, height=600)
screen.tracer(0)  # 关闭自动刷新,手动更新提升流畅度

# 初始化双方玩家载具
p1 = turtle.Turtle(shape="triangle")
p1.color("blue")
p1.penup()
p1.goto(-100, 0)
p1.direction = 0  # 初始朝向:右
p1.pensize(3)

p2 = turtle.Turtle(shape="triangle")
p2.color("red")
p2.penup()
p2.goto(100, 0)
p2.direction = 180  # 初始朝向:左
p2.pensize(3)

# 初始化双方尾迹坐标集合,存入整数格式的初始坐标
MOVE_STEP = 5  # 固定移动步长,必须为整数
BORDER_LIMIT = 280  # 撞墙边界阈值
p1_trail = set()
p2_trail = set()
p1_trail.add((int(p1.xcor()), int(p1.ycor())))
p2_trail.add((int(p2.xcor()), int(p2.ycor())))

# 方向控制函数,禁止180度直接掉头
def p1_turn_up():
    if p1.direction != 270:
        p1.direction = 90
def p1_turn_down():
    if p1.direction != 90:
        p1.direction = 270
def p1_turn_left():
    if p1.direction != 0:
        p1.direction = 180
def p1_turn_right():
    if p1.direction != 180:
        p1.direction = 0

def p2_turn_up():
    if p2.direction != 270:
        p2.direction = 90
def p2_turn_down():
    if p2.direction != 90:
        p2.direction = 270
def p2_turn_left():
    if p2.direction != 0:
        p2.direction = 180
def p2_turn_right():
    if p2.direction != 180:
        p2.direction = 0

# 绑定按键(P1用WASD,P2用方向键)
screen.listen()
screen.onkeypress(p1_turn_up, "w")
screen.onkeypress(p1_turn_down, "s")
screen.onkeypress(p1_turn_left, "a")
screen.onkeypress(p1_turn_right, "d")
screen.onkeypress(p2_turn_up, "Up")
screen.onkeypress(p2_turn_down, "Down")
screen.onkeypress(p2_turn_left, "Left")
screen.onkeypress(p2_turn_right, "Right")

# 玩家移动函数,移动完成后自动追加坐标到对应尾迹集合
def move_player(player, trail_set):
    player.setheading(player.direction)
    player.pendown()  # 落下画笔绘制可见尾迹
    player.forward(MOVE_STEP)
    # 坐标转整数元组存入集合,规避浮点精度问题
    current_pos = (int(player.xcor()), int(player.ycor()))
    trail_set.add(current_pos)
    return current_pos

# 主游戏循环
game_running = True
while game_running:
    screen.update()
    # 移动双方玩家
    p1_current = move_player(p1, p1_trail)
    p2_current = move_player(p2, p2_trail)

    # 碰撞判定
    # P1撞墙/撞P2尾迹,P2获胜
    if abs(p1_current[0]) > BORDER_LIMIT or abs(p1_current[1]) > BORDER_LIMIT or p1_current in p2_trail:
        p1.penup()
        p1.goto(0, 0)
        p1.write("红方获胜", align="center", font=("黑体", 32, "bold"))
        game_running = False
    # P2撞墙/撞P1尾迹,P1获胜
    if abs(p2_current[0]) > BORDER_LIMIT or abs(p2_current[1]) > BORDER_LIMIT or p2_current in p1_trail:
        p2.penup()
        p2.goto(0, 0)
        p2.write("蓝方获胜", align="center", font=("黑体", 32, "bold"))
        game_running = False
    # 双方坐标重合,对撞平局
    if p1_current == p2_current:
        p1.penup()
        p1.goto(0, 0)
        p1.write("双方同归于尽", align="center", font=("黑体", 32, "bold"))
        game_running = False

screen.mainloop()
关键注意事项
  • 禁止在move_player内部重新赋值轨迹集合变量,否则会创建局部变量覆盖外层传入的集合,导致坐标无法正常追加。
  • 移动步长必须设为整数,配合坐标转int的逻辑,完全规避浮点计算带来的坐标匹配误差。
  • 必须等玩家移动完成后再向集合追加坐标,不要在移动前追加,否则会漏判最后一步的碰撞。
  • 不需要额外判定撞自己尾迹:玩家每次移动的新坐标必然不在自己的旧轨迹里,只要没有瞬移逻辑,自己的轨迹永远是连续不回头的,不会出现自己撞自己的情况。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 15:21:26