请求修复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。
额外问题修复
代码还存在几个影响运行的问题,一并修复:
you_died中新建Canvas会导致界面叠加,重启后死亡界面无法消失;collision_detection中尝试修改pig_bbox元组元素(元组不可变,操作无效);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()
关键修改说明
- 全局变量声明:
restart_game中添加global pig_item,确保新创建的猪元素被全局引用; - 定时器管理:新增定时器ID变量,重启时用
after_cancel清除旧任务,避免速度异常; - 死亡界面优化:直接复用原画布绘制死亡提示,解决界面叠加问题;
- 碰撞检测健壮性:添加
pig_bbox非空判断,避免元素删除后报错; - 无效操作修复:将修改
pig_bbox元组的代码改为移动猪元素,实现顶部边界限制。
内容的提问来源于stack exchange,提问作者HelpMePlease
相关产品推荐
相关产品推荐

