前一阵子,我带着大家做了一个简单的弹球实例,现在我们来对弹球游戏进行优化加强,让游戏的功能变得更好玩吧
弹球游戏2.0展示效果
实现流程
1.主循环,游戏画布
2.创建ball类,增加几个动作(让小球移动、让小球来回反弹,改变小球的起始方向)
3.创建Paddle类,加上球拍,使球拍左右移动(循环移动)
4.创建Score类增加输赢因素(加入3次游戏机会)
导入模块、包
import tkinter as tkinter
import tkinter.messagebox as mb
import random
import time
导入tkinter,random、time
tkinter实现画布弹窗,random实现随机出现,time负责等待sleep()
主循环
def main():
tk = tkinter.Tk()
def callback():
if mb.askokcancel("退出", "你想退出吗?"):
tk.destroy()
tk.protocol("WM_DELETE_WINDOW", callback)
canvas_width = 500
canvas_hight = 500
tk.title("弹球游戏")
tk.resizable(0, 0)
tk.wm_attributes("-topmost", 1)
canvas = tkinter.Canvas(tk, width=canvas_width, height=canvas_hight, bd=0, highlightthickness=0, bg='#00ffff')
canvas.pack()
tk.update()
score = Score(canvas, 'red')
paddle = Paddle(canvas, "red")
ball = Ball(canvas, paddle, score, "grey")
game_over_text = canvas.create_text(canvas_width / 2, canvas_hight / 2, text='游戏结束', state='hidden', fill='red',
font=(None, 18, "bold"))
introduce = '欢迎来到弹球游戏\n 按任意键开始\n'
game_start_text = canvas.create_text(canvas_width / 2, canvas_hight / 2, text=introduce, state='normal',
fill='magenta', font=(None, 18, "bold"))
while True:
if (ball.hit_bottom == False) and ball.paddle.started:
canvas.itemconfig(game_start_text, state='hidden')
ball.draw()
paddle.draw()
if ball.hit_bottom == True:
time.sleep(0.1)
canvas.itemconfig(game_over_text, state='normal')
tk.update_idletasks()
tk.update()
time.sleep(0.01)
if __name__ == '__main__':
main()
tkinter.Tk()类创建一个tk对象,它就是一个基本窗口,可以在其上增加其他东西
该函数用于,当点击窗口关闭按钮时,展示一个消息提示框,询问是否要关闭,点击是,则退出窗口
protocol:主要用于窗体关闭监听,通过这个协议可以在窗体关闭前提示是否真的要关闭,这样可以防止用户误触导致数据的丢失
我们需要增加一个动画循环 “主循环”是程序的中心部分,一般来讲它控制程序中大部分的行为。
我们的主循环目前只是让tkinter 重画屏幕。这个循环一直运行下去,不停地让tkinter重画屏幕,然后休息百分之一秒。
如果小球没有碰到了底部,且游戏尚未开始
如果小球碰到了底部,则游戏结束
创建ball类
class Ball():
def __init__(self, canvas, paddle, score, color, init_x=100, init_y=100):
self.canvas = canvas
self.paddle = paddle
self.score = score
self.color = color
self.id = canvas.create_oval(10, 10, 30, 30, fill=self.color)
self.canvas.move(self.id, init_x, init_y)
starts = [-3, -2, -1, 1, 2, 3]
random.shuffle(starts)
self.x = starts[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.hit_bottom = False
def draw(self):
self.canvas.move(self.id, self.x, self.y)
position = self.canvas.coords(self.id)
if position[1] <= 0:
self.y = 3
if position[3] >= self.canvas_height:
self.hit_bottom = True
if self.hit_paddle(position) == True:
self.y = -3
if position[0] <= 0:
self.x = 3
if position[2] >= self.canvas_width:
self.x = -3
#该函数用于让小球水平和垂直运动,在运动的过程中,判断是否得分、游戏是否结束
def hit_paddle(self, position):
paddle_position = self.canvas.coords(self.paddle.id)
print('paddle_position:', paddle_position[0], paddle_position[1], paddle_position[2], paddle_position[3])
if position[2] >= paddle_position[0] and position[0] <= paddle_position[2]:
if position[3] >= paddle_position[1] and position[3] <= paddle_position[3]:
self.x += self.paddle.x
colors = ['red', 'green']
# shuffle() 方法将序列的所有元素随机排序,以便随机获得小球颜色
random.shuffle(colors)
self.color = colors[0]
self.canvas.itemconfig(self.id, fill=colors[0])
self.score.hit(ball_color=self.color)
self.canvas.itemconfig(self.paddle.id, fill=self.color)
self.adjust_paddle(paddle_position)
return True
#该函数用于判断球是否碰到了球拍,如果碰到了则使小球回弹,否则游戏结束
shuffle() 方法将序列的所有元素随机排序,以便随机获得小球颜色
return False
def adjust_paddle(self, paddle_position):
paddle_grow_length = 30
paddle_width = paddle_position[2] - paddle_position[0]
if self.color == 'red':
if paddle_width > 30:
if paddle_position[2] >= self.canvas_width:
paddle_position[2] = paddle_position[2] - paddle_grow_length
else:
paddle_position[0] = paddle_position[0] + paddle_grow_length
elif self.color == 'green':
if paddle_width < 300:
if paddle_position[2] >= self.canvas_width:
paddle_position[0] = paddle_position[0] - paddle_grow_length
else:
paddle_position[2] = paddle_position[2] + paddle_grow_length
创建Ball类,初始化对象,即创建对象设置属性
init函数是在对象被创建的同时就设置属性的一种方法,Python会在创建新对象时自动调用这个函数
Ball类初始化属性
canvas:画布
paddle:球拍
score:得分
color:小球的颜色
init_x:小球球的初始横坐标
init_y:小球球的初始纵坐标
shuffle() 方法将序列的所有元素随机排序
winfo_height()函数来获取画布当前的高度,赋值给对象变量
小球是否碰触到画布底部,初始值为False,即没有碰到
创建Paddle类
class Paddle:
def __init__(self, canvas, color):
self.canvas = canvas
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.id = canvas.create_rectangle(0, 0, 180, 15, fill=color)
self.canvas.move(self.id, 200, self.canvas_height * 0.75)
self.x = 0
self.started = False
self.continue_game = False
self.canvas.bind_all('<KeyPress-Left>', self.turn_left)
self.canvas.bind_all('<KeyPress-Right>', self.turn_right)
self.canvas.bind_all('<KeyPress-Enter>', self.continue_game)
self.canvas.bind_all('<Button-1>', self.start_game)
self.canvas.bind_all('<space>', self.pause_game)
def turn_left(self, event):
position = self.canvas.coords(self.id)
if position[0] <= 0:
self.x = 0
else:
self.x = -3
def turn_right(self, event):
position = self.canvas.coords(self.id)
if position[2] >= self.canvas_width:
self.x = 0
else:
self.x = 3
#该两个函数函数用于向左(右)移动时
获取球拍的位置坐标
如果球拍的左(右)上角x坐标 小于 0
则再次按向左(右)移动时,移动距离为0
每次向左(右)移动3个像素
def start_game(self, evt):
self.started = True
def pause_game(self, evt):
if self.started:
self.started = False
else:
self.started = True
def draw(self):
self.canvas.move(self.id, self.x, 0)
position = self.canvas.coords(self.id)
if position[0] <= 0:
self.x = 0
elif position[2] >= self.canvas_width:
self.x = 0
#该函数用于移动球拍,球拍类可以水平移动
获取球拍的位置坐标,
如果球拍左上角x坐标小于等于0,则停止移动
如果球拍右下角x坐标大于等于0,则停止移动
winfo_width()函数来获取画布当前的宽度,赋值给对象变量
是否继续游戏,默认值为 否
初始化时将事件‘按下左键'和函数向左移动绑定
初始化时将事件‘按下右键'和函数向右移动绑定
初始化时将事件‘按下Enter键'和函数继续游戏绑定
按任意键开始游戏
初始化时将事件‘按下space键'和函数暂停游戏绑定
创建Score类
class Score():
def __init__(self, canvas, color):
self.score = 0
self.canvas = canvas
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.id = canvas.create_text(self.canvas_width - 150, 10, text='score:0', fill=color, font=(None, 18, "bold"))
self.note = canvas.create_text(self.canvas_width - 70, 10, text='--', fill='red', font=(None, 18, "bold"))
def hit(self, ball_color='grey'):
self.score += 1
self.canvas.itemconfig(self.id, text='得分:{}'.format(self.score))
if ball_color == 'red':
self.canvas.itemconfig(self.note, text='{}-'.format('颜色'), fill='red')
elif ball_color == 'green':
self.canvas.itemconfig(self.note, text='{}+'.format('颜色'), fill='green')
else:
self.canvas.itemconfig(self.note, text='--', fill='grey')
得分类
canvas:画布
color:得分文本的颜色
winfo_width()函数来获取画布当前的宽度,赋值给对象变量
创建文本控件,用户保存用户保存得分
用户保存游戏的关卡颜色
该函数用于将计算得分并展示,且同时将小球的颜色、关卡颜色同步
代码展示、可copy
import tkinter as tkinter
import tkinter.messagebox as mb
import random
import time
class Ball():
def __init__(self, canvas, paddle, score, color, init_x=100, init_y=100):
self.canvas = canvas
self.paddle = paddle
self.score = score
self.color = color
self.id = canvas.create_oval(10, 10, 30, 30, fill=self.color)
self.canvas.move(self.id, init_x, init_y)
starts = [-3, -2, -1, 1, 2, 3]
random.shuffle(starts)
self.x = starts[0]
self.y = -3
self.canvas_height = self.canvas.winfo_height()
self.canvas_width = self.canvas.winfo_width()
self.hit_bottom = False
def draw(self):
self.canvas.move(self.id, self.x, self.y)
position = self.canvas.coords(self.id)
if position[1] <= 0:
self.y = 3
if position[3] >= self.canvas_height:
self.hit_bottom = True
if self.hit_paddle(position) == True:
self.y = -3
if position[0] <= 0:
self.x = 3
if position[2] >= self.canvas_width:
self.x = -3
def hit_paddle(self, position):
paddle_position = self.canvas.coords(self.paddle.id)
print('paddle_position:', paddle_position[0], paddle_position[1], paddle_position[2], paddle_position[3])
if position[2] >= paddle_position[0] and position[0] <= paddle_position[2]:
if position[3] >= paddle_position[1] and position[3] <= paddle_position[3]:
self.x += self.paddle.x
colors = ['red', 'green']
# shuffle() 方法将序列的所有元素随机排序,以便随机获得小球颜色
random.shuffle(colors)
self.color = colors[0]
self.canvas.itemconfig(self.id, fill=colors[0])
self.score.hit(ball_color=self.color)
self.canvas.itemconfig(self.paddle.id, fill=self.color)
self.adjust_paddle(paddle_position)
return True
return False
def adjust_paddle(self, paddle_position):
paddle_grow_length = 30
paddle_width = paddle_position[2] - paddle_position[0]
if self.color == 'red':
if paddle_width > 30:
if paddle_position[2] >= self.canvas_width:
paddle_position[2] = paddle_position[2] - paddle_grow_length
else:
paddle_position[0] = paddle_position[0] + paddle_grow_length
elif self.color == 'green':
if paddle_width < 300:
if paddle_position[2] >= self.canvas_width:
paddle_position[0] = paddle_position[0] - paddle_grow_length
else:
paddle_position[2] = paddle_position[2] + paddle_grow_length
class Paddle:
def __init__(self, canvas, color):
self.canvas = canvas
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.id = canvas.create_rectangle(0, 0, 180, 15, fill=color)
self.canvas.move(self.id, 200, self.canvas_height * 0.75)
self.x = 0
self.started = False
self.continue_game = False
self.canvas.bind_all('<KeyPress-Left>', self.turn_left)
self.canvas.bind_all('<KeyPress-Right>', self.turn_right)
self.canvas.bind_all('<KeyPress-Enter>', self.continue_game)
self.canvas.bind_all('<Button-1>', self.start_game)
self.canvas.bind_all('<space>', self.pause_game)
def turn_left(self, event):
position = self.canvas.coords(self.id)
if position[0] <= 0:
self.x = 0
else:
self.x = -3
def turn_right(self, event):
position = self.canvas.coords(self.id)
if position[2] >= self.canvas_width:
self.x = 0
else:
self.x = 3
def start_game(self, evt):
self.started = True
def pause_game(self, evt):
if self.started:
self.started = False
else:
self.started = True
def draw(self):
self.canvas.move(self.id, self.x, 0)
position = self.canvas.coords(self.id)
if position[0] <= 0:
self.x = 0
elif position[2] >= self.canvas_width:
self.x = 0
class Score():
def __init__(self, canvas, color):
self.score = 0
self.canvas = canvas
self.canvas_width = self.canvas.winfo_width()
self.canvas_height = self.canvas.winfo_height()
self.id = canvas.create_text(self.canvas_width - 150, 10, text='score:0', fill=color, font=(None, 18, "bold"))
self.note = canvas.create_text(self.canvas_width - 70, 10, text='--', fill='red', font=(None, 18, "bold"))
def hit(self, ball_color='grey'):
self.score += 1
self.canvas.itemconfig(self.id, text='得分:{}'.format(self.score))
if ball_color == 'red':
self.canvas.itemconfig(self.note, text='{}-'.format('颜色'), fill='red')
elif ball_color == 'green':
self.canvas.itemconfig(self.note, text='{}+'.format('颜色'), fill='green')
else:
self.canvas.itemconfig(self.note, text='--', fill='grey')
def main():
tk = tkinter.Tk()
def callback():
if mb.askokcancel("退出", "你想退出吗?"):
tk.destroy()
tk.protocol("WM_DELETE_WINDOW", callback)
canvas_width = 500
canvas_hight = 500
tk.title("弹球游戏")
tk.resizable(0, 0)
tk.wm_attributes("-topmost", 1)
canvas = tkinter.Canvas(tk, width=canvas_width, height=canvas_hight, bd=0, highlightthickness=0, bg='#00ffff')
canvas.pack()
tk.update()
score = Score(canvas, 'red')
paddle = Paddle(canvas, "red")
ball = Ball(canvas, paddle, score, "grey")
game_over_text = canvas.create_text(canvas_width / 2, canvas_hight / 2, text='游戏结束', state='hidden', fill='red',
font=(None, 18, "bold"))
introduce = '欢迎来到弹球游戏\n 按任意键开始\n'
game_start_text = canvas.create_text(canvas_width / 2, canvas_hight / 2, text=introduce, state='normal',
fill='magenta', font=(None, 18, "bold"))
while True:
if (ball.hit_bottom == False) and ball.paddle.started:
canvas.itemconfig(game_start_text, state='hidden')
ball.draw()
paddle.draw()
if ball.hit_bottom == True:
time.sleep(0.1)
canvas.itemconfig(game_over_text, state='normal')
tk.update_idletasks()
tk.update()
time.sleep(0.01)
if __name__ == '__main__':
main()