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

如何在Tkinter Canvas中实现小球相互碰撞反弹效果

问题原因
  • 碰撞检测逻辑错误:当前使用==判断两个球的边框完全对齐才触发反弹,小球每次移动1像素,很容易出现坐标错位漏检,或是碰撞后两球已经重叠还反复触发方向翻转,直接导致卡顿、来回抖动。
  • 动画刷新逻辑不符合Tkinter规范:用无限while True+time.sleep的写法会阻塞Tkinter原生的事件循环,是Tkinter动画场景的常见错误写法。
  • 多余IO操作:循环内每次都打印两个球的坐标,频繁的控制台输出也会拖慢运行速度。
修复方案
  1. 把碰撞检测改为圆形碰撞判定:计算两个球的圆心距离,当距离小于等于两个球的半径之和时触发反弹,避免边界对齐判定的漏检问题。
  2. 替换无限循环为Tkinter原生的after()方法实现定时刷新,避免阻塞事件循环。
  3. 删除循环内的打印语句。
修复后代码

Ball类代码

class Ball:
    def __init__ (self,canvas,x,y,diameter,xVelo,yVelo,color):
        self.canvas = canvas
        self.image = canvas.create_oval(x,y,diameter,diameter,fill=color)
        self.xVelo = xVelo
        self.yVelo = yVelo
        self.radius = (diameter - x)/2 # 提前存储半径方便后续碰撞计算
    def move(self):
        coordinates = self.canvas.coords(self.image)
        if (coordinates[2] >= self.canvas.winfo_width() or coordinates[0] < 0):
            self.xVelo = -self.xVelo
        if (coordinates[3] >= self.canvas.winfo_height() or coordinates[1] < 0):
            self.yVelo = -self.yVelo
        self.canvas.move(self.image,self.xVelo,self.yVelo)

主程序代码

from tkinter import *
from Ball import *
HEIGHT = 500
WIDTH = 500
window = Tk()

canvas = Canvas(window,height=HEIGHT,width = WIDTH)
canvas.pack()
basket_ball = Ball(canvas,0,0,100,1,1,"orange")
soccer_ball = Ball(canvas,399,399,499,1,1,"black")

def update_frame():
    basket_ball_coords = canvas.coords(basket_ball.image)
    soccer_ball_coords = canvas.coords(soccer_ball.image)
    basket_ball.move()
    soccer_ball.move()
    
    # 计算两球圆心坐标
    b_center_x = (basket_ball_coords[0] + basket_ball_coords[2])/2
    b_center_y = (basket_ball_coords[1] + basket_ball_coords[3])/2
    s_center_x = (soccer_ball_coords[0] + soccer_ball_coords[2])/2
    s_center_y = (soccer_ball_coords[1] + soccer_ball_coords[3])/2
    # 计算圆心距离平方(避免开根号提升性能)
    distance_sq = (b_center_x - s_center_x)**2 + (b_center_y - s_center_y)**2
    min_distance = basket_ball.radius + soccer_ball.radius
    
    if distance_sq <= min_distance**2:
        # 碰撞触发,翻转x、y速度
        basket_ball.xVelo = -basket_ball.xVelo
        basket_ball.yVelo = -basket_ball.yVelo
        soccer_ball.xVelo = -soccer_ball.xVelo
        soccer_ball.yVelo = -soccer_ball.yVelo
    
    window.after(10, update_frame) # 10ms刷新一次,对应原来的0.01s间隔

update_frame()
window.mainloop()

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 22:54:03