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

请求修复Python Tkinter游戏的碰撞检测与重启函数问题

Flappy Pig游戏碰撞检测错误修复方案

错误原因

报错TypeError: 'NoneType' object is not subscriptable是因为collision_detection函数中pig_bbox = canvas.bbox(pig_item)返回了None。核心问题:

  • 游戏首次启动创建的pig_item在you_died中被canvas.delete("all")删除;
  • restart_game函数中创建新pig_item时未声明为全局变量,导致全局引用仍指向已删除的元素,调用bbox自然返回None。

额外问题修复

代码还存在几个影响运行的问题,一并修复:

  1. you_died中新建Canvas会导致界面叠加,重启后死亡界面无法消失;
  2. collision_detection中尝试修改pig_bbox元组元素(元组不可变,操作无效);
  3. game函数重复调用定时器启动逻辑,导致重启后柱子生成、计分速度翻倍。

修改后的完整代码

from tkinter import *
from random import randint
from PIL import Image, ImageTk
import os

window = Tk()
window.title("Flappy Pig")
window.iconbitmap('flypig.ico')
screenwidth = window.winfo_screenwidth()
screenheight = window.winfo_screenheight()
x = (screenwidth / 2) - 250
y = (screenheight / 2) - 300
window.geometry(f'500x600+{int(x)}+{int(y)}')

canvas = Canvas(window, width=500, height=600, background="#ADD8E6")
canvas.pack()

farm_img = Image.open("farm2.png")
farmresized = farm_img.resize((600, 150))
newfarm = ImageTk.PhotoImage(farmresized)
farm = canvas.create_image(200, 450, image=newfarm)

# 初始化猪元素
Pig_Y = 250
pig = Image.open("flypig.png")
pigresized = pig.resize((100, 100))
pig_img = ImageTk.PhotoImage(pigresized)
pig_item = canvas.create_image(200, Pig_Y, image=pig_img)

bgbottom = canvas.create_rectangle(0, 535, 500, 600, fill="grey", outline="grey")

topPoles = []
bottomPoles = []
poleSpeed = -1.5
lifeStatus = "alive"
# 定时器ID变量,用于重启时清除旧任务
create_poles_after_id = None
score_level_after_id = None
pig_fall_after_id = None
poles_move_after_id = None

def create_poles():
    global create_poles_after_id
    if lifeStatus == "alive":
        x2 = randint(0, 385)
        y1 = x2 + 150
        topPoles.append(canvas.create_rectangle(470, 0, 500, x2, fill="green", outline="green"))
        bottomPoles.append(canvas.create_rectangle(470, y1, 500, 535, fill="green", outline="green"))
    create_poles_after_id = window.after(2000, create_poles)

def poles_move():
    global poles_move_after_id
    if lifeStatus == "alive":
        for i in topPoles:
            canvas.move(i, poleSpeed, 0)
        for j in bottomPoles:
            canvas.move(j, poleSpeed, 0)
    poles_move_after_id = window.after(10, poles_move)

def pig_move(event):
    if lifeStatus == "alive":
        canvas.move(pig_item, 0, -60)

window.bind('<space>', pig_move)

def pig_fall():
    global pig_fall_after_id
    if lifeStatus == "alive":
        canvas.move(pig_item, 0, 4)
    pig_fall_after_id = window.after(20, pig_fall)

def you_died():
    global lifeStatus
    canvas.delete("all")
    topPoles.clear()
    bottomPoles.clear()
    lifeStatus = "dead"
    # 直接在原画布绘制死亡界面,避免叠加
    canvas.config(background="red")
    canvas.create_text(250, 250, text="You Died!", font=('Helvetica', '30', 'bold'))
    canvas.create_text(250, 350, text="press x to restart", font=('Helvetica', 20))

def collision_detection():
    if lifeStatus != "alive":
        return
    pig_bbox = canvas.bbox(pig_item)
    # 空值判断,避免已删除元素报错
    if not pig_bbox:
        return
    # 检测与下柱子碰撞
    for bottom_pole in bottomPoles:
        coords_bottom_pole = canvas.bbox(bottom_pole)
        if coords_bottom_pole and pig_bbox[0] < coords_bottom_pole[2] and pig_bbox[2] > coords_bottom_pole[0] and pig_bbox[1] < coords_bottom_pole[3] and pig_bbox[3] > coords_bottom_pole[1]:
            you_died()
            return
    # 检测与上柱子碰撞
    for top_pole in topPoles:
        coords_top_pole = canvas.bbox(top_pole)
        if coords_top_pole and pig_bbox[0] < coords_top_pole[2] and pig_bbox[2] > coords_top_pole[0] and pig_bbox[1] < coords_top_pole[3] and pig_bbox[3] > coords_top_pole[1]:
            you_died()
            return
    # 检测碰地面
    if pig_bbox[3] > 525:
        you_died()
        return
    # 检测碰顶部边界,改为移动猪元素
    if pig_bbox[1] < -100:
        canvas.move(pig_item, 0, 50)

def restart_game():
    global lifeStatus, pig_item, Score, score_text
    # 清除所有旧定时器,避免任务叠加
    global create_poles_after_id, score_level_after_id, pig_fall_after_id, poles_move_after_id
    if create_poles_after_id:
        window.after_cancel(create_poles_after_id)
    if score_level_after_id:
        window.after_cancel(score_level_after_id)
    if pig_fall_after_id:
        window.after_cancel(pig_fall_after_id)
    if poles_move_after_id:
        window.after_cancel(poles_move_after_id)
    
    Score = 0
    lifeStatus = "alive"
    canvas.delete("all")
    canvas.config(background="#ADD8E6")
    canvas.create_image(200, 450, image=newfarm)
    canvas.create_rectangle(0, 535, 500, 600, fill="grey", outline="grey")
    # 声明全局pig_item,覆盖旧引用
    pig_item = canvas.create_image(200, Pig_Y, image=pig_img)
    score_text = canvas.create_text(35, 25, text=Score, font=('Helvetica', '30', 'bold'))
    topPoles.clear()
    bottomPoles.clear()
    game()

def game():
    pig_fall()
    poles_move()
    # 循环调用碰撞检测
    def collision_loop():
        collision_detection()
        if lifeStatus == "alive":
            window.after(10, collision_loop)
    collision_loop()
    score_level()
    create_poles()

Score = 0
score_text = canvas.create_text(35, 25, text=Score, font=('Helvetica', '30', 'bold'))

def score_level():
    global Score, score_level_after_id
    if lifeStatus == "alive":
        Score += 1
        canvas.itemconfig(score_text, text=Score)
    score_level_after_id = window.after(2000, score_level)

window.bind('x', lambda event: restart_game())
game()
window.mainloop()

关键修改说明

  1. 全局变量声明:restart_game中添加global pig_item,确保新创建的猪元素被全局引用;
  2. 定时器管理:新增定时器ID变量,重启时用after_cancel清除旧任务,避免速度异常;
  3. 死亡界面优化:直接复用原画布绘制死亡提示,解决界面叠加问题;
  4. 碰撞检测健壮性:添加pig_bbox非空判断,避免元素删除后报错;
  5. 无效操作修复:将修改pig_bbox元组的代码改为移动猪元素,实现顶部边界限制。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 23:20:10