第13章 你的第一个游戏:弹球
13.1 打击反弹球
我们将要开发一个有反弹球和球拍构成的游戏。球会在屏幕上飞过来,玩家要用球拍把它弹回去。如果球落到了屏幕底部,那么游戏就结束了。
13.2 创建游戏画布
from tkinter import *
import random
import time
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
resizable函数是窗口的大小不可调整,其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变。wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)。bd=0和highlightthickness=0确保画布之外没有边框。tk.update()让tkinter为我们游戏中的动画做好初始化,如果没有这一行,我们看到的东西会和期望的不一样
13.3 创建Ball类
思路:
- 创建一个叫Ball的类,它有两个参数,一个是画布,一个是求的颜色。
- 把画布保存到一个对象变量中,因为我们会在它上面画球。
- 在画布上画一个用颜色参数作为填充色的小球。
- 把tkinter画小球时所返回的ID保存起来,因为我们要用它来移动屏幕上的小球。
承接13.2中的代码:
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,color):
self.canvas = canvas
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
def draw(self):
pass
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建红色小球对象
ball = Ball(canvas,'red')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
tk.update_idletasks()
tk.update()
time.sleep(0.01)
在关闭游戏窗口时,你可能会见到Shell程序中打印出错误信息。这是因为当你关闭窗口时,代码要强行从while循环中跳出来,Python觉得不爽。
13.4 增加几个动作
13.4.1 让小球移动
修改draw函数,并在主循环中调用
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,color):
self.canvas = canvas
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
self.canvas.move(self.id,0,-1)
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建红色小球对象
ball = Ball(canvas,'red')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
在关闭游戏窗口时,你可能会见到Shell程序中打印出错误信息。这是因为当你关闭窗口时,代码要强行从while循环中跳出来,Python觉得不爽。
13.4.2 让小球来回反弹
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,color):
self.canvas = canvas
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
self.x = 0
self.y = -1
# winfo_height函数用来获取画布当前的高度
self.canvas_height = self.canvas.winfo_height()
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
#self.canvas.move(self.id,0,-1)
self.canvas.move(self.id,self.x,self.y)
# coords函数返回一个由四个数字组成的列表来表示坐标。
# 比如:[255.0, 29.0, 270.0, 44.0]
# 前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
pos = self.canvas.coords(self.id)
# y1=0 说明小球撞到了屏幕顶部,接需要向下移动了,故使self.y=1
if pos[1] <= 0:
self.y = 1
# y2=self.canvas_height 说明小球撞到了屏幕底部,接需要向上移动了,故使self.y=-1
if pos[3] >= self.canvas_height:
self.y = -1
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建红色小球对象
ball = Ball(canvas,'red')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
coords函数返回一个由四个数字组成的列表来表示坐标。print(self.canvas.coords(self.id)) 打印出来可以看到返回值[255.0, 29.0, 270.0, 44.0],前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
13.4.3 改变小球的起始方向
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,color):
self.canvas = canvas
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
starts = [-3,-2,-1,1,2,3]
random.shuffle(starts)
# x有可能是列表中的任意一个值,从-3到3
self.x = starts[0]
# 把y改成-3,让小球飞快一点
self.y = -3
# winfo_height函数用来获取画布当前的高度
self.canvas_height = self.canvas.winfo_height()
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
#self.canvas.move(self.id,0,-1)
self.canvas.move(self.id,self.x,self.y)
# coords函数返回一个由四个数字组成的列表来表示坐标。
# 比如:[255.0, 29.0, 270.0, 44.0]
# 前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
pos = self.canvas.coords(self.id)
# y1=0 说明小球撞到了屏幕顶部,接下来需要向下移动了,故使self.y=3
if pos[1] <= 0:
self.y = 3
# y2=self.canvas_height 说明小球撞到了屏幕底部,接下来需要向上移动了,故使self.y=-3
if pos[3] >= self.canvas_height:
self.y = -3
# x1=0 说明小球撞到了屏幕最左端,接下来需要向右移动了,故使self.x=3
if pos[0] <= 0:
self.x = 3
# x2=0 说明小球撞到了屏幕最右端,接下来需要向左移动了,故使self.x=-3
if pos[2] >= self.canvas_width:
self.x = -3
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建红色小球对象
ball = Ball(canvas,'red')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
ball.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
第14章 完成你的第一个游戏:反弹吧,小球!
14.1 加上球拍
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,color):
self.canvas = canvas
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
starts = [-3,-2,-1,1,2,3]
random.shuffle(starts)
# x有可能是列表中的任意一个值,从-3到3
self.x = starts[0]
# 把y改成-3,让小球飞快一点
self.y = -3
# winfo_height函数用来获取画布当前的高度
self.canvas_height = self.canvas.winfo_height()
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
#self.canvas.move(self.id,0,-1)
self.canvas.move(self.id,self.x,self.y)
# coords函数返回一个由四个数字组成的列表来表示坐标。
# 比如:[255.0, 29.0, 270.0, 44.0]
# 前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
pos = self.canvas.coords(self.id)
# y1=0 说明小球撞到了屏幕顶部,接下来需要向下移动了,故使self.y=3
if pos[1] <= 0:
self.y = 3
# y2=self.canvas_height 说明小球撞到了屏幕底部,接下来需要向上移动了,故使self.y=-3
if pos[3] >= self.canvas_height:
self.y = -3
# x1=0 说明小球撞到了屏幕最左端,接下来需要向右移动了,故使self.x=3
if pos[0] <= 0:
self.x = 3
# x2=0 说明小球撞到了屏幕最右端,接下来需要向左移动了,故使self.x=-3
if pos[2] >= self.canvas_width:
self.x = -3
class Paddle:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_rectangle(0,0,100,10,fill=color)
self.canvas.move(self.id,200,300)
def draw(self):
pass
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建红色小球对象
ball = Ball(canvas,'red')
# 创建蓝色球拍
paddle = Paddle(canvas,'blue')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
14.2 让球拍移动
要想让球拍左右移动,我们要用事件绑定来把左右方向键绑定到Paddle类的新函数上。当玩家按向左键时,变量x会被设置为-2(向左移),按向右键时,变量x会被设置为2(向右移)。
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,color):
self.canvas = canvas
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
starts = [-3,-2,-1,1,2,3]
random.shuffle(starts)
# x有可能是列表中的任意一个值,从-3到3
self.x = starts[0]
# 把y改成-3,让小球飞快一点
self.y = -3
# winfo_height函数用来获取画布当前的高度
self.canvas_height = self.canvas.winfo_height()
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
#self.canvas.move(self.id,0,-1)
self.canvas.move(self.id,self.x,self.y)
# coords函数返回一个由四个数字组成的列表来表示坐标。
# 比如:[255.0, 29.0, 270.0, 44.0]
# 前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
pos = self.canvas.coords(self.id)
# y1=0 说明小球撞到了屏幕顶部,接下来需要向下移动了,故使self.y=3
if pos[1] <= 0:
self.y = 3
# y2=self.canvas_height 说明小球撞到了屏幕底部,接下来需要向上移动了,故使self.y=-3
if pos[3] >= self.canvas_height:
self.y = -3
# x1=0 说明小球撞到了屏幕最左端,接下来需要向右移动了,故使self.x=3
if pos[0] <= 0:
self.x = 3
# x2=0 说明小球撞到了屏幕最右端,接下来需要向左移动了,故使self.x=-3
if pos[2] >= self.canvas_width:
self.x = -3
class Paddle:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_rectangle(0,0,100,10,fill=color)
self.canvas.move(self.id,200,300)
self.x = 0
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
# 绑定向左键、向右键的按下事件
self.canvas.bind_all('',self.turn_left)
self.canvas.bind_all('',self.turn_right)
def draw(self):
self.canvas.move(self.id,self.x,0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -2
def turn_right(self, evt):
self.x = 2
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建红色小球对象
ball = Ball(canvas,'red')
# 创建蓝色球拍
paddle = Paddle(canvas,'blue')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
14.3 判断小球是否击中球拍
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,paddle,color):
self.canvas = canvas
self.paddle = paddle
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
starts = [-3,-2,-1,1,2,3]
random.shuffle(starts)
# x有可能是列表中的任意一个值,从-3到3
self.x = starts[0]
# 把y改成-3,让小球飞快一点
self.y = -3
# winfo_height函数用来获取画布当前的高度
self.canvas_height = self.canvas.winfo_height()
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
#self.canvas.move(self.id,0,-1)
self.canvas.move(self.id,self.x,self.y)
# coords函数返回一个由四个数字组成的列表来表示坐标。
# 比如:[255.0, 29.0, 270.0, 44.0]
# 前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
pos = self.canvas.coords(self.id)
# y1=0 说明小球撞到了屏幕顶部,接下来需要向下移动了,故使self.y=3
if pos[1] <= 0:
self.y = 3
# y2=self.canvas_height 说明小球撞到了屏幕底部,接下来需要向上移动了,故使self.y=-3
if pos[3] >= self.canvas_height:
self.y = -3
if self.hit_paddle(pos) == True:
self.y = -3
# x1=0 说明小球撞到了屏幕最左端,接下来需要向右移动了,故使self.x=3
if pos[0] <= 0:
self.x = 3
# x2=0 说明小球撞到了屏幕最右端,接下来需要向左移动了,故使self.x=-3
if pos[2] >= self.canvas_width:
self.x = -3
def hit_paddle(self,pos):
paddle_pos = self.canvas.coords(self.paddle.id)
# 如果小球的右侧大于球拍的左侧,并且小球的左侧小于球拍的右侧
if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]:
# 小球的底部(pos[3])在球拍的顶部(paddle_pos[1])和底部(paddle_pos[3])之间
if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
return True
return False
class Paddle:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_rectangle(0,0,100,10,fill=color)
self.canvas.move(self.id,200,300)
self.x = 0
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
# 绑定向左键、向右键的按下事件
self.canvas.bind_all('',self.turn_left)
self.canvas.bind_all('',self.turn_right)
def draw(self):
self.canvas.move(self.id,self.x,0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -2
def turn_right(self, evt):
self.x = 2
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建蓝色球拍
paddle = Paddle(canvas,'blue')
# 创建红色小球对象
ball = Ball(canvas,paddle,'red')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)
为什么要看小球的底部是否在球拍的顶部和底部之间,而不是直接判断小球的底部是否打到了球拍的顶部?因为小球在屏幕上每次移动3个 像素,如果我们只检查了小球是否到达了球拍的顶部,我们可能已经跨过了那个位置,这样的话小球仍会向前移动,穿过球拍,不会停止。
14.3 增加输赢因素
小球落到屏幕底部时,Game over
from tkinter import *
import random
import time
class Ball:
def __init__(self,canvas,paddle,color):
self.canvas = canvas
self.paddle = paddle
# create_oval函数中,(10,10)左上角坐标,(25,25)右下角坐标
self.id = canvas.create_oval(10,10,25,25,fill=color)
# 把椭圆形移到画布的中心(245,100)
# 画布之所以知道要移动什么,是因为我们用保存好的形状ID来标识它
self.canvas.move(self.id,245,100)
starts = [-3,-2,-1,1,2,3]
random.shuffle(starts)
# x有可能是列表中的任意一个值,从-3到3
self.x = starts[0]
# 把y改成-3,让小球飞快一点
self.y = -3
# winfo_height函数用来获取画布当前的高度
self.canvas_height = self.canvas.winfo_height()
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
self.hit_bottom = False
def draw(self):
# id是椭圆形的id,0是不要水平移动,-1是在屏幕上向上移动1个像素
#self.canvas.move(self.id,0,-1)
self.canvas.move(self.id,self.x,self.y)
# coords函数返回一个由四个数字组成的列表来表示坐标。
# 比如:[255.0, 29.0, 270.0, 44.0]
# 前两个数字是椭圆形左上角的坐标(x1,y1),后两个数字是椭圆形右下角的坐标(x2,y2)
pos = self.canvas.coords(self.id)
# y1=0 说明小球撞到了屏幕顶部,接下来需要向下移动了,故使self.y=3
if pos[1] <= 0:
self.y = 3
# y2=self.canvas_height 说明小球撞到了屏幕底部
if pos[3] >= self.canvas_height:
#self.y = -3
self.hit_bottom = True
if self.hit_paddle(pos) == True:
self.y = -3
# x1=0 说明小球撞到了屏幕最左端,接下来需要向右移动了,故使self.x=3
if pos[0] <= 0:
self.x = 3
# x2=0 说明小球撞到了屏幕最右端,接下来需要向左移动了,故使self.x=-3
if pos[2] >= self.canvas_width:
self.x = -3
def hit_paddle(self,pos):
paddle_pos = self.canvas.coords(self.paddle.id)
# 如果小球的右侧大于球拍的左侧,并且小球的左侧小于球拍的右侧
if pos[2] >= paddle_pos[0] and pos[0] <= paddle_pos[2]:
# 小球的底部(pos[3])在球拍的顶部(paddle_pos[1])和底部(paddle_pos[3])之间
if pos[3] >= paddle_pos[1] and pos[3] <= paddle_pos[3]:
return True
return False
class Paddle:
def __init__(self,canvas,color):
self.canvas = canvas
self.id = canvas.create_rectangle(0,0,100,10,fill=color)
self.canvas.move(self.id,200,300)
self.x = 0
# winfo_width函数用来获取画布当前的宽度
self.canvas_width = self.canvas.winfo_width()
# 绑定向左键、向右键的按下事件
self.canvas.bind_all('',self.turn_left)
self.canvas.bind_all('',self.turn_right)
def draw(self):
self.canvas.move(self.id,self.x,0)
pos = self.canvas.coords(self.id)
if pos[0] <= 0:
self.x = 0
elif pos[2] >= self.canvas_width:
self.x = 0
def turn_left(self, evt):
self.x = -2
def turn_right(self, evt):
self.x = 2
tk = Tk()
tk.title('Game')
# resizable函数是窗口的大小不可调整
# 其中参数(0,0)的意思是窗口的大小在水平方向和垂直方向上都不能改变
tk.resizable(0,0)
# wm_attributes告诉tkinter把包含我们画布的窗口放到所有其他窗口之前(-topmost)
tk.wm_attributes('-topmost',1)
# bd=0和highlightthickness=0确保画布之外没有边框
canvas = Canvas(tk,width=500,height=400,bd=0,highlightthickness=0)
canvas.pack()
# tk.update()让tkinter为我们游戏中的动画做好初始化
# 如果没有这一行,我们看到的东西会和期望的不一样
tk.update()
# 创建蓝色球拍
paddle = Paddle(canvas,'blue')
# 创建红色小球对象
ball = Ball(canvas,paddle,'red')
# 防止窗口马上关闭,增加一个动画循环,我们把它成为我们游戏的“主循环”
# 这个主循环目前只是不停地让tkinter重画屏幕,然后休息百分之一秒
while 1:
if ball.hit_bottom == False:
ball.draw()
paddle.draw()
tk.update_idletasks()
tk.update()
time.sleep(0.01)