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

如何在Python Turtle模块中检测两个Turtle对象的碰撞?

Turtle对象碰撞检测问题修复

问题说明

作为Python和计算机科学新手,我在制作Turtle游戏时,尝试检测玩家对象p1和管道对象pipe1的碰撞,但程序完全忽略了碰撞检测代码,没有触发lose()函数。

原始代码

import turtle,time,random, math

#setup
wn = turtle.Screen()
p1 = turtle.Turtle()
pipe1 = turtle.Turtle()
pipe2 = turtle.Turtle()
pipe3 = turtle.Turtle()
pipe4 = turtle.Turtle()
msg = turtle.Turtle()
BG = turtle.Turtle()
BG2 = turtle.Turtle()
is_not_jump = True
Lose = False
p1.penup()
p1.goto(0,-50)
p1.speed(-1)
pipe1.speed(-1)
pipe2.speed(-1)
pipe3.speed(-1)
pipe4.speed(-1)
msg.speed(-1)
BG.speed(-1)
BG2.speed(-1)
wn.addshape("Sprites//P1.gif")
wn.addshape("Sprites//P1R.gif")
wn.addshape("Sprites//P1L.gif")
wn.addshape("Sprites//P1D.gif")
wn.addshape("Sprites//pipe.gif")
wn.addshape("Sprites//Pipe1.gif")
wn.addshape("Sprites//Pipe2.gif")
wn.addshape("Sprites//Pipe3.gif")
wn.addshape("Sprites//Pipe4.gif")
wn.addshape("Sprites//Back.gif")
wn.addshape("Sprites//Coin.gif")
BG.shape("Sprites//Back.gif")
BG2.shape("Sprites//Back.gif")
p1.shape("Sprites//P1.gif")
pipe1.shape("Sprites//Pipe1.gif")
pipe2.shape("Sprites//Pipe2.gif")
pipe3.shape("Sprites//Pipe3.gif")
pipe4.shape("Sprites//Pipe4.gif")
BG.goto(0,0)
BG.penup()
BG2.goto(0,700)
BG2.penup()
pipe1.penup()
pipe1.goto(87.5,350)
pipe2.penup()
pipe2.goto(-87.5,550)
pipe3.penup()
pipe3.goto(87.5,700)
pipe4.penup()
pipe4.goto(-87.5,850)
msg.hideturtle()
msg.penup()
msg.goto(0,50)
msg.write("Press Space to start!", align="Center", font=
          ("Comic Sans MS", 14, "bold"))
wn.setup(350,700)
p1.speed(0)
p1.goto(0,-50)
BG.goto(0,0)


#Controls
#def is_touch_y_up():
#    if p1.ycor()+28 <= pipe1.ycor-14:
#        return True
#    else:
#        return False

def p1_left():
        p1.shape("Sprites//P1L.gif")
        msg.clear()
        x = p1.xcor()-15
        p1.goto(x,p1.ycor())
def p1_right():
        p1.shape("Sprites//P1R.gif")
        msg.clear()
        x = p1.xcor()+15
        p1.goto(x,p1.ycor())
def fall():
    BG.setheading(270)
    BG2.setheading(270)
    pipe1.setheading(270)
    pipe2.setheading(270)
    pipe3.setheading(270)
    pipe4.setheading(270)
    while True:
        BG.forward(1)
        BG2.forward(1)
        pipe1.forward(2.5)
        pipe2.forward(2.5)
        pipe3.forward(2.5)
        pipe4.forward(2.5)
        if pipe1.ycor() <= -350:
            pipe1.goto(87.5,350)
        if pipe2.ycor() <= -350:
            pipe2.goto(-87.5,350)
        if pipe3.ycor() <= -350:
            pipe3.goto(87.5,350)
        if pipe4.ycor() <= -350:
            pipe4.goto(-87.5,350)
        if BG.ycor() <= -700:
            BG.goto(0,700)
        if BG2.ycor() <= -700:
            BG2.goto(0,700)
        if p1.ycor() <= -350:
            p1.goto(p1.xcor(),325)
        if p1.ycor() >= 350:
            p1.goto(p1.xcor(),-325)
        if is_not_jump:
            p1.setheading(270)
            p1.forward(5)
        if is_touch_y_up:
            lose()


def lose():
  while True:
    #pipe1.forward(0.01)
    #pipe2.forward(0.01)
    #pipe3.forward(0.01)
    #pipe4.forward(0.01)
    #pipe1.forward(-0.01)
    #pipe2.forward(-0.01)
    #pipe3.forward(-0.01)
    #pipe4.forward(-0.01)
    #BG.forward(0.01)
    #BG.forward(-0.01)
    #BG2.forward(0.01)
    #BG2 .forward(-0.01)
    is_not_jump == False
    wn.onkey(None,"Left")
    wn.onkey(None,"Right")
    wn.onkey(None, "space")
    p1.shape("Sprites//P1D.gif")
    msg.write("You lose...\nReopen this game to play again", align="Center", font=
          ("Comic Sans MS", 14, "bold"))

def jump():
    p1.shape("Sprites//P1.gif")
    msg.clear()
    is_not_jump = False
    p1.setheading(90)
    p1.forward(75)
    is_not_jump = True
    setup_keys()
    fall()


def setup_keys():   
        wn.onkey(lose,"r")
        wn.onkey(p1_left,"Left")
        wn.onkey(p1_right,"Right")


wn.onkey(jump, "space")
wn.listen()




turtle.mainloop()

问题分析与修复方案

1. 碰撞检测函数的错误

你注释的is_touch_y_up函数存在两个问题:

  • pipe1.ycor是方法,必须调用pipe1.ycor()才能获取坐标值
  • 逻辑错误,该函数的条件无法正确判断碰撞,应该改为检测两个对象的距离或坐标重叠范围

修复后的碰撞检测函数:

def is_collided():
    # 根据精灵大小调整距离阈值
    collision_threshold = 30
    if p1.distance(pipe1) < collision_threshold:
        return True
    if p1.distance(pipe2) < collision_threshold:
        return True
    if p1.distance(pipe3) < collision_threshold:
        return True
    if p1.distance(pipe4) < collision_threshold:
        return True
    return False

2. 碰撞检测的调用错误

在fall()函数里,if is_touch_y_up:只是引用函数对象,不会执行判断逻辑,必须改为调用函数:

if is_collided():
    lose()

3. 全局变量作用域问题

is_not_jump是全局变量,但在jump()和lose()函数里修改时,默认会创建局部变量,需要用global声明:

def jump():
    global is_not_jump
    # ...其他代码

def lose():
    global is_not_jump
    # ...其他代码

4. lose()函数的赋值错误

is_not_jump == False是比较操作,不是赋值,应该改为is_not_jump = False

5. 避免死循环阻塞事件循环

fall()里的while True会阻塞Turtle的事件监听,导致按键无响应,改用turtle.ontimer()实现动画循环:

def fall():
    # ...原有移动逻辑
    if is_collided():
        lose()
        return  # 碰撞后停止循环
    
    wn.ontimer(fall, 10)  # 每10毫秒调用一次fall,实现动画

6. lose()函数的死循环问题

lose()里的while True会阻塞程序,移除后直接执行逻辑即可:

def lose():
    global is_not_jump
    is_not_jump = False
    wn.onkey(None,"Left")
    wn.onkey(None,"Right")
    wn.onkey(None, "space")
    p1.shape("Sprites//P1D.gif")
    msg.write("You lose...\nReopen this game to play again", align="Center", font=
          ("Comic Sans MS", 14, "bold"))

修复后的完整代码

import turtle, math

#setup
wn = turtle.Screen()
p1 = turtle.Turtle()
pipe1 = turtle.Turtle()
pipe2 = turtle.Turtle()
pipe3 = turtle.Turtle()
pipe4 = turtle.Turtle()
msg = turtle.Turtle()
BG = turtle.Turtle()
BG2 = turtle.Turtle()
is_not_jump = True
Lose = False

p1.penup()
p1.goto(0,-50)
p1.speed(-1)
pipe1.speed(-1)
pipe2.speed(-1)
pipe3.speed(-1)
pipe4.speed(-1)
msg.speed(-1)
BG.speed(-1)
BG2.speed(-1)

wn.addshape("Sprites//P1.gif")
wn.addshape("Sprites//P1R.gif")
wn.addshape("Sprites//P1L.gif")
wn.addshape("Sprites//P1D.gif")
wn.addshape("Sprites//pipe.gif")
wn.addshape("Sprites//Pipe1.gif")
wn.addshape("Sprites//Pipe2.gif")
wn.addshape("Sprites//Pipe3.gif")
wn.addshape("Sprites//Pipe4.gif")
wn.addshape("Sprites//Back.gif")
wn.addshape("Sprites//Coin.gif")

BG.shape("Sprites//Back.gif")
BG2.shape("Sprites//Back.gif")
p1.shape("Sprites//P1.gif")
pipe1.shape("Sprites//Pipe1.gif")
pipe2.shape("Sprites//Pipe2.gif")
pipe3.shape("Sprites//Pipe3.gif")
pipe4.shape("Sprites//Pipe4.gif")

BG.goto(0,0)
BG.penup()
BG2.goto(0,700)
BG2.penup()
pipe1.penup()
pipe1.goto(87.5,350)
pipe2.penup()
pipe2.goto(-87.5,550)
pipe3.penup()
pipe3.goto(87.5,700)
pipe4.penup()
pipe4.goto(-87.5,850)

msg.hideturtle()
msg.penup()
msg.goto(0,50)
msg.write("Press Space to start!", align="Center", font=
          ("Comic Sans MS", 14, "bold"))

wn.setup(350,700)
p1.speed(0)
p1.goto(0,-50)
BG.goto(0,0)

# 碰撞检测函数
def is_collided():
    # 根据精灵大小调整距离阈值
    collision_threshold = 30
    if p1.distance(pipe1) < collision_threshold:
        return True
    if p1.distance(pipe2) < collision_threshold:
        return True
    if p1.distance(pipe3) < collision_threshold:
        return True
    if p1.distance(pipe4) < collision_threshold:
        return True
    return False

def p1_left():
    p1.shape("Sprites//P1L.gif")
    msg.clear()
    x = p1.xcor()-15
    p1.goto(x,p1.ycor())

def p1_right():
    p1.shape("Sprites//P1R.gif")
    msg.clear()
    x = p1.xcor()+15
    p1.goto(x,p1.ycor())

def fall():
    BG.setheading(270)
    BG2.setheading(270)
    pipe1.setheading(270)
    pipe2.setheading(270)
    pipe3.setheading(270)
    pipe4.setheading(270)

    BG.forward(1)
    BG2.forward(1)
    pipe1.forward(2.5)
    pipe2.forward(2.5)
    pipe3.forward(2.5)
    pipe4.forward(2.5)

    if pipe1.ycor() <= -350:
        pipe1.goto(87.5,350)
    if pipe2.ycor() <= -350:
        pipe2.goto(-87.5,350)
    if pipe3.ycor() <= -350:
        pipe3.goto(87.5,350)
    if pipe4.ycor() <= -350:
        pipe4.goto(-87.5,350)

    if BG.ycor() <= -700:
        BG.goto(0,700)
    if BG2.ycor() <= -700:
        BG2.goto(0,700)

    if p1.ycor() <= -350:
        p1.goto(p1.xcor(),325)
    if p1.ycor() >= 350:
        p1.goto(p1.xcor(),-325)

    if is_not_jump:
        p1.setheading(270)
        p1.forward(5)

    if is_collided():
        lose()
        return
    
    wn.ontimer(fall, 10)

def lose():
    global is_not_jump
    is_not_jump = False
    wn.onkey(None,"Left")
    wn.onkey(None,"Right")
    wn.onkey(None, "space")
    p1.shape("Sprites//P1D.gif")
    msg.write("You lose...\nReopen this game to play again", align="Center", font=
          ("Comic Sans MS", 14, "bold"))

def jump():
    global is_not_jump
    p1.shape("Sprites//P1.gif")
    msg.clear()
    is_not_jump = False
    p1.setheading(90)
    p1.forward(75)
    is_not_jump = True
    setup_keys()
    fall()

def setup_keys():   
    wn.onkey(lose,"r")
    wn.onkey(p1_left,"Left")
    wn.onkey(p1_right,"Right")

wn.onkey(jump, "space")
wn.listen()

turtle.mainloop()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 16:57:02