Python Turtle Tron游戏 记录移动坐标到集合实现碰撞检测
问题根因
集合仅保存初始位置、不追加移动坐标的问题,基本是踩了这三个坑:
- 作用域错误:在
move_player函数内部重新定义了同名的轨迹集合变量,实际操作的是函数局部的空集合,没有修改外层初始化的正式轨迹集合。 - 数据类型错误:直接存储
turtle.pos()返回的浮点型Vec2D对象,一方面turtle内部浮点计算存在微小精度误差,看似相同的坐标实际值差了万分之一个像素,导致集合匹配失败;另一方面未做类型转换时,每次生成的新Vec2D对象哈希值不统一,集合判定逻辑失效。 - 执行时机错误:把轨迹追加逻辑写在了移动分支外、或者碰撞判定逻辑之后,移动操作完成后根本没触发坐标追加代码。
可直接运行的正确实现方案
核心逻辑是固定整数步长移动、坐标转整数元组存集合、移动完成后立刻追加轨迹、双向判定碰撞。
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
相关产品推荐
相关产品推荐

