如何在Tkinter中检测碰撞后删除单个食物椭圆对象
Tkinter碰撞检测失效问题:玩家触碰食物无法删除单个食物
我用Tkinter编写了一段代码,可通过A、D键控制Label组件(玩家)左右移动,希望实现玩家与Canvas绘制的oval(食物)坐标重合时,仅删除被触碰的单个食物对象,但当前碰撞检测逻辑未生效。
原代码
from tkinter import * food_cords= [] wind = Tk() wind.geometry("1600x850") canvas = Canvas(wind,width = 1600,height=850) canvaswide = [1600,850] canvas.pack() canvas.create_line(0,700,1600,700,fill = "black",width = 5,tag="line") def move_right(event): label.place(x=label.winfo_x() + 10, y=label.winfo_y()) x1 = label.winfo_x() print(x1) wind.update() if 1600 == x1: wind.bind("<d>", move_left) == wind.bind("<d>", nothing()) def move_left(event): label.place(x=label.winfo_x() - 10, y=label.winfo_y()) x1 = label.winfo_x() print(x1) wind.update() if 0 == x1: wind.bind("<a>", move_left) == wind.bind("<a>", nothing) def nothing(event): pass wind.bind("<d>",move_right) wind.bind("<a>",move_left) bg = PhotoImage(file="python2.png") mybg = canvas.create_image(0,-580,image=bg,anchor=NW) label = Label(wind, image=bg) label.place(x=0, y=580) #y1 = label.winfo_y() class Food(): y = food_cords[0] + 20 x = food_cords[1] - 40 global label #global food_cords if cords == line_coordinates: for index in food_cords: if index == food_cords[0]: y = index + 20 while food_cords[1] > 0: x = food_cords[1] - 40 food_cords[1] = food_cords[1] - 100 oval = canvas.create_oval(0, 0, 30, 30, fill="yellow",tag="foood") canvas.moveto(oval, x=x, y=y) print(2312321313) print(x) print(label.winfo_x()) print(y) print(label.winfo_y()) if x == label.winfo_x() and y == label.winfo_y: print(1313123454565675643) canvas.delete(oval) wind.update() canvas.pack() food = Food() wind.mainloop()
问题分析
Food类逻辑完全失效:
food_cords是空列表,直接取food_cords[0]会引发索引错误,且类属性初始化方式错误cords和line_coordinates未定义,导致食物创建的if条件永远不成立,根本没有食物被生成- 碰撞检测仅在食物创建时执行一次,玩家移动后不会重新检查碰撞
label.winfo_y少了括号,错误地将方法对象与数值比较,永远不会触发删除
移动边界控制逻辑错误:
- 用
==判断绑定事件是无效操作,且nothing()直接调用函数,应该传入函数名 move_left函数外的边界判断代码永远不会执行,因为x1是函数内的局部变量
- 用
修复后的代码
from tkinter import * wind = Tk() wind.geometry("1600x850") canvas = Canvas(wind, width=1600, height=850) canvas.pack() # 绘制地面线 canvas.create_line(0, 700, 1600, 700, fill="black", width=5, tag="line") # 存储所有食物的ID和坐标 food_list = [] def move_right(event): new_x = label.winfo_x() + 10 # 限制玩家不超出右边界 if new_x <= 1600 - label.winfo_width(): label.place(x=new_x, y=label.winfo_y()) check_collision() def move_left(event): new_x = label.winfo_x() - 10 # 限制玩家不超出左边界 if new_x >= 0: label.place(x=new_x, y=label.winfo_y()) check_collision() def nothing(event): pass # 碰撞检测函数:每次移动后检查所有食物 def check_collision(): player_x = label.winfo_x() player_y = label.winfo_y() player_width = label.winfo_width() player_height = label.winfo_height() # 倒序遍历,避免删除元素后索引混乱 for idx in reversed(range(len(food_list))): food_id, food_x, food_y = food_list[idx] # 判断玩家与食物是否重叠(考虑玩家和食物的尺寸) if (player_x < food_x + 30 and player_x + player_width > food_x and player_y < food_y + 30 and player_y + player_height > food_y): canvas.delete(food_id) del food_list[idx] # 初始化玩家 bg = PhotoImage(file="python2.png") label = Label(wind, image=bg) label.place(x=0, y=580) # 生成食物:在地面线上方生成一排黄色食物 class Food(): def __init__(self): # 从右往左生成,间隔100像素 start_x = 1500 while start_x > 0: food_y = 700 - 50 # 地面线上方50像素 oval = canvas.create_oval(start_x, food_y, start_x+30, food_y+30, fill="yellow") food_list.append((oval, start_x, food_y)) start_x -= 100 food = Food() # 绑定按键事件 wind.bind("<d>", move_right) wind.bind("<a>", move_left) wind.mainloop()
关键改动说明
- 用
food_list存储每个食物的ID和坐标,方便后续碰撞检测和删除 - 将碰撞检测独立成
check_collision函数,每次玩家移动后调用,实时检查碰撞 - 修复边界控制逻辑,直接限制玩家移动范围,避免无效的事件绑定操作
- 改进碰撞检测逻辑,考虑玩家和食物的实际尺寸(不再用严格坐标相等,而是判断矩形重叠)
- 重构Food类的初始化逻辑,正确生成一排食物
内容的提问来源于stack exchange,提问作者coder34321553667
相关产品推荐
相关产品推荐

