Tkinter Canvas 球拍与球碰撞的处理
在上述程序的执行结果中,球碰到球拍基本上是可以穿透过去,本节将讲解碰撞的处理。首先可以增加将Racket类传给Ball类,如下所示。
class Ball:
def __init__(self, canvas, color, winW, winH, racket):
self.canvas = canvas
self.racket = racket
当然在主程序中建立Ball类对象时需修改调用方法如下。
racket = Racket(canvas, 'purple') # 定义紫色球拍
ball = Ball(canvas, 'yellow', winW, winH, racket) # 定义球对象
在Ball类中需增加是否球碰到球拍的方法,如果碰到就让球沿路径往上反弹。
if self.hitRacket(ballPos) == True: # 侦测是否撞到球拍
self.y = -step
在Ball类的ballMove( )方法上方需增加下列hitRacket( )方法,检测球是否碰撞球拍,如果碰撞了会传回True,否则传回False。
def hitRacket(self,ballPos):
racketPos = self.canvas.coords(self.racket.id)
if ballPos[2] >= racketPos[0] and ballPos[0] <= racketPos[2]:
if ballPos[3] >= racketPos[1] and ballPos[3] <= racketPos[3]:
return True
return False
上述侦测是否球撞到球拍必须符合以下两个条件。
(1)球的右侧x轴坐标ballPos[2]大于球拍左侧x坐标racketPos[0],同时球的左侧x坐标ballPos[0]小于球拍右侧x坐标racketPos[2]。
(2)球的下方y坐标ballPos[3]大于球拍上方的y坐标racketPos[1],同时必须小于球拍下方的y坐标racketPos[3]。读者可能奇怪为何不是侦测碰到球拍上方即可,主要是球不是一次移动1像素,如果移动3像素,很可能会跳过球拍上方。
下面是球的可能移动方式图示。
示例1
当球碰撞到球拍时会反弹。下面是完整的Ball类设计。
from tkinter import *
from random import *
import time
class Ball:#class Ball():#这样写是一样的
def __init__(self,canvas,color,winW,winH,racket):
self.canvas = canvas
self.racket = racket
self.id = canvas.create_oval(0, 0, 20, 20, fill=color) # 建立球对象
self.canvas.move(self.id,winW/2,winH/2) # 设置球最初位置
startPos = [-4, -3, -2, -1, 1, 2, 3, 4] # 球最初x轴位移的随机数
shuffle(startPos) # 打乱排序
self.x = startPos[0] # 球最初水平移动单位
self.y = step # 垂直移动单位
def hitRacket(self,ballPos):
racketPos = self.canvas.coords(self.racket.id)
if ballPos[2] >= racketPos[0] and ballPos[0] <= racketPos[2]:
if ballPos[3] >= racketPos[1] and ballPos[3] <= racketPos[3]:
return True
return False
def ballMove(self):
self.canvas.move(self.id, self.x, self.y) # step是正值表示往下移动
ballPos = self.canvas.coords(self.id)
if ballPos[0] <= 0: # 侦测球是否超过画布左方
self.x = step
if ballPos[1] <= 0: # 侦测球是否超过画布上方
self.y = step
if ballPos[2] >= winW: # 侦测球是否超过画布右方
self.x = -step
if ballPos[3] >= winH: # 侦测球是否超过画布下方
self.y = -step
if self.hitRacket(ballPos): # 侦测是否撞到球拍
self.y = -step
class Racket():
def __init__(self, canvas, color):
self.canvas = canvas
self.id = canvas.create_rectangle(0,0,100,15,fill=color) # 球拍对象
self.canvas.move(self.id, 270, 400)
self.x = 0
self.canvas.bind_all('<KeyPress-Right>',self.moveRight) # 绑定按住右键 ##########################
self.canvas.bind_all('<KeyPress-Left>',self.moveLeft) # 绑定按住左键
def racketMove(self):
self.canvas.move(self.id,self.x,0)
pos = self.canvas.coords(self.id)
# self.x = 0
if pos[0] <= 0:
self.x = 0
elif pos[2] >= winW:
self.x = 0
def moveLeft(self,event):
print("Press the left button")
self.x = -3
def moveRight(self,event):
print("Press the right button")
self.x = 3
winW = 640 # 定义画布宽度
winH = 480 # 定义画布高度
step = 3 # 定义速度可想成位移步长
speed = 0.03 # 设置移动速度
tk = Tk()
tk.title("Bouncing Ball apidemos") # 游戏窗口标题
tk.wm_attributes('-topmost',1) # 确保游戏窗口在屏幕最上层
canvas = Canvas(tk,width=winW, height=winH) # 建立画布
canvas.pack()
tk.update()
# 以下两行代码执行顺序决定了两者重合时哪一个对象被遮挡
racket = Racket(canvas,'purple') # 定义紫色球拍
ball = Ball(canvas,'yellow',winW,winH,racket) # 定义球对象
while True:
ball.ballMove()
racket.racketMove()
tk.update()
time.sleep(speed) # 可以控制移动速度
# tk.mainloop()
输出: