目录
- 1、引言
- 2、系统结构
- 2.1 总体结构
- 2.2 局部结构
- 2.2.1 main模块结构
- 2.2.2 Bird模块结构
- 2.2.3 Tubing模块结构
- 3、代码实现
- 4、实验
- 5、总结和展望
1、引言
本次论文主要是基于对像素鸟游戏的研究。
像素鸟是一款风靡一时的小游戏,这款游戏架构简单,可以通过不同的编程语言去实现。近段时间学了python这门编程语言。于是突发奇想用python去实现这款游戏。
该程序主要实现的功能有:双击运行程序,播放背景音乐。出现游戏界面,通过不断按下键盘上面的 ↑ 键,使得小鸟飞行。每次按下 ↑ 键,就会播放小鸟翅膀挥舞的声音。通过小鸟的飞行,越过障碍物(管子),可以得到分数。每越过一个障碍物就可以得到一分。如果小鸟与障碍物发生碰撞,或者小鸟飞出游戏界面,那么就会播放gameover的声音,同时停止游戏,出现游戏结束界面。在游戏界面界面,你会看到历史最高分和你的分数,如果你的分数高于历史最高分,将会被记录在record.txt文件中。同时,在游戏结束界面有两个图片按钮,你可以通过点击鼠标左键来选择重新开始游戏或者是结束游戏。
2、系统结构
2.1 总体结构
本程序主要分成了3个模块,main模块是主函数模块,Bird模块是小鸟对象模块继承了(sprite),Tubing模块是管子对象模块也继承了(sprite)
2.2 局部结构
2.2.1 main模块结构
main模块中主要实现加载背景图片,加载游戏中需要的音乐。判断小鸟精灵和管子组精灵是否发生碰撞,实现小鸟按键飞行,播放音乐。绘制游戏结束后的界面以及按键事件、鼠标点击事件、与退出事件。
2.2.2 Bird模块结构
Bird模块主要实现关于小鸟对象的图片加载(__init__函数),小鸟显示的位置(__init__函数),小鸟的飞翔(fly函数),小鸟的下降(drop函数)
2.2.3 Tubing模块结构
Tubing模块主要实现了上面的管子(Tubing_up类)和下面的管子(Tubing_down类)的图片加载(__init__函数)、管子显示的位置(__init__函数)、管子的移动(move函数)
3、代码实现
先导入pygame模块,初始化pygame
import pygame
pygame.init()
创建游戏窗口,设置游戏窗口标题
bg_size = width,height = 903,495
view = pygame.display.set_mode(bg_size)
pygame.display.set_caption("像素鸟小游戏")
加载背景图片、背景音乐、绘制背景图片在游戏窗口上显示、播放背景音乐
import sys
#初始化pygame的扩音器
pygame.mixer.init()
#加载背景音乐
pygame.mixer.music.load("music/主界面BGM.mp3")
#加载背景图片
background = pygame.image.load("image/BGM.png").convert()
def main():
#finish定义游戏是否结束(结束则为True)
finish = False
#开始循环播放背景音乐
pygame.mixer.music.play(-1)
while True:
#如果点击了关闭事件就退出程序
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#如果游戏还没结束
if finish == False:
#绘制背景图片
view.blit(background,(0,0))
#如果游戏已经结束,停止背景音乐的播放,绘制游戏结束后的界面
if finish == True:
pygame.mixer.music.stop()
#更新界面
pygame.display.flip()
#延迟10毫秒
pygame.time.delay(10)
if __name__ == "__main__":
main()
新建一个模块实现小鸟对象
import pygame
#继承Sprite精灵类,方便用于碰撞检测
class Bird(pygame.sprite.Sprite):
def __init__(self,bg_size):
pygame.sprite.Sprite.__init__(self)
#加载图片
self.img1 = pygame.image.load("image/bird1.png").convert_alpha()
self.img2 = pygame.image.load("image/bird2.png").convert_alpha()
self.img3 = pygame.image.load("image/bird3.png").convert_alpha()
#设置小鸟的初始位置为窗口1/4、高度1/2的位置
self.width, self.height = bg_size[0], bg_size[1]
self.rect = self.img1.get_rect()
self.rect.left, self.rect.top = self.width // 4, self.height // 2
#设置小鸟的初速度、加速度、路程、时间
self.v = 20
self.g = 2
self.s = 0
self.t = 0.2
#小鸟向下飞行的方法
def drop(self):
self.v = self.v-self.g*self.t
self.s = self.v*self.t - 0.5*self.g*self.t*self.t
self.rect.top = self.rect.top-self.s
#小鸟向上飞行的方法
def fly(self):
self.v = 10
在主函数里面绘制小鸟,并实现小鸟的下落和按键后小鸟飞翔,以及小鸟飞翔挥动翅膀的声音
import Bird
#加载小鸟挥动翅膀的声音、小鸟撞击的声音
wings_sound = pygame.mixer.Sound("music/翅膀声.wav")
crash_sound = pygame.mixer.Sound("music/me_down.wav")
def main():
#设置小鸟飞行挥动翅膀图片的切换flag
change = 1
#创建一个小鸟对象
bird = Bird.Bird(bg_size)
while True:
#如果游戏还没结束
if finish == False:
#如果小鸟飞行超过上边界或者超过下边界,就播放撞击音乐,把finish置为True,表示程序结束
if bird.rect.top < 0 or bird.rect.bottom > width:
crash_sound.play()
finish = True
#切换小鸟飞行图片,以达到小鸟挥动翅膀的效果
if change % 3 == 0:
view.blit(bird.img1,bird.rect) #绘制小鸟图片
elif change % 3 == 1:
view.blit(bird.img2,bird.rect)
else:
view.blit(bird.img3,bird.rect)
change = change +1
#调用小鸟对象的下落方法
bird.drop()
#如果键盘上面的↑按键被按下,就调用小鸟对象的向上飞行的方法,同时播放小鸟挥动翅膀的声音
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_UP]:
wings_sound.play()
bird.fly()
#更新界面
pygame.display.flip()
#延迟10毫秒
pygame.time.delay(10)
if __name__ == "__main__":
main()
新建一个Tubing模块实现管子对象
import pygame
import random
#检测Sprite精灵类,方便用于碰撞检测
class Tubing_down(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
#加载图片,设置下面管子的显示位置
self.pipe = pygame.image.load("image/管子.png").convert_alpha()
self.rect = self.pipe.get_rect()
self.rect.left = 903
self.rect.top = random.randint(120,495-40)
#管子的移动方法
def move(self):
self.rect.left = self.rect.left - 1
class Tubing_up(pygame.sprite.Sprite):
def __init__(self,down_pipe):
#加载图片,设置下面管子的显示位置,根据下面的管子来设置上面管子显示的位置,预留一些空间给小鸟飞行
self.pipe = pygame.image.load("image/管子.png").convert_alpha()
self.rect = self.pipe.get_rect()
self.rect.left = 903
self.rect.top = -(640-down_pipe.rect.top+120)
#管子的移动方法
def move(self):
self.rect.left = self.rect.left - 1
在main模块里面实现管子的产生、绘制以及小鸟和管子发生碰撞的一系列事件
import Tubing
from pygame.locals import *
#定义存放管子的数组,tubeUp_array代表存放上面的那条管子,tubeDown_array代表存放下面的那条管子
tubeUp_array = []
tubeDown_array = []
def main():
#设置开始时间,用于每隔3秒就产生一对管子
start_time = 0
while True:
#如果游戏还没结束
if finish == False:
#每3秒产生一对管子,一上一下,分别存放在ubeUp_array、tubeDown_array列表中
end_time = pygame.time.get_ticks()
if end_time - start_time >= 3000:
tubeDown = Tubing.Tubing_down()
tubeUp = Tubing.Tubing_up(tubeDown)
tubeDown_array.append(tubeDown)
tubeUp_array.append(tubeUp)
start_time = end_time
#判断小鸟精灵和管子精灵组有没有发生碰撞,如果发生了碰撞就播放碰撞音乐,同时将finish置为True
Up_crash = pygame.sprite.spritecollide(bird, tubeUp_array, False)
Down_crash = pygame.sprite.spritecollide(bird, tubeDown_array, False)
if Up_crash or Down_crash:
crash_sound.play()
finish = True
#显示管子和移动管子
for i in range(len(tubeDown_array)):
view.blit(tubeDown_array[i].pipe,tubeDown_array[i].rect)
view.blit(tubeUp_array[i].pipe,tubeUp_array[i].rect)
tubeDown_array[i].move()
tubeUp_array[i].move()
if __name__ == "__main__":
main()
实现小鸟飞过一对管子就分数+1,同时把分数绘制显示出来
#设置分数显示的字体、
font = pygame.font.Font("font/font.ttf",35)
def main():
#设置游戏的分数
score = 0
while True:
#如果游戏还没结束
if finish == False:
#小鸟每飞过一对管子分数就加1,显示管子和移动管子
for i in range(len(tubeDown_array)):
if bird.rect.right == tubeUp_array[i].rect.right:
score = score +1
#绘制分数
font_view = font.render('score = %s' %str(score),True,(255,255,255))
view.blit(font_view,(10,5))
#更新界面
pygame.display.flip()
#延迟10毫秒
pygame.time.delay(10)
if __name__ == "__main__":
main()
绘制游戏结束后的界面
from pygame.locals import *
#设置游戏结束之后显示最高分数等字的字体
gameover_font = pygame.font.Font("font/font.ttf", 48)
#加载游戏结束后重新开始按键图片、游戏结束后退出游戏按键图片
again_image = pygame.image.load("image/again.png").convert_alpha()
gameover_image = pygame.image.load("image/gameover.png").convert_alpha()
def main():
#reader用于判断record.txt(存储最高分)的文件是否已经被打开(已经打开为True)
reader = False
#获取重新开始按键的方框、获取结束游戏按键的方框
again_rect = again_image.get_rect()
gameover_rect = gameover_image.get_rect()
while True:
#如果游戏已经结束,停止背景音乐的播放,绘制游戏结束后的界面
if finish == True:
#如果record.txt文件还未打开过,打开record.txt文件,将reader 置为 True
if not reader:
reader = True
# 读取历史最高分
with open("record.txt", "r") as f:
record_score = int(f.read())
#判断当前分数是否高于历史最高分,如果是,则写入record.txt文件中,同时将record_score赋值为最高分
if score > record_score:
with open("record.txt", "w") as f:
record_score = score
f.write(str(score))
#绘制游戏结束后的历史最高分
record_score_text = font.render("Best: %d" % record_score, True, (255,255,255))
view.blit(record_score_text, (50, 50))
#绘制游戏结束后的我的分数
gameover_text1 = gameover_font.render("Your Score: ", True, (255,255,255))
gameover_text1_rect = gameover_text1.get_rect()
gameover_text1_rect.left, gameover_text1_rect.top = \
(width - gameover_text1_rect.width) // 2, height // 3
view.blit(gameover_text1, gameover_text1_rect)
gameover_text2 = gameover_font.render(str(score), True, (255,255,255))
gameover_text2_rect = gameover_text2.get_rect()
gameover_text2_rect.left, gameover_text2_rect.top = \
(width - gameover_text2_rect.width) // 2, \
gameover_text1_rect.bottom + 10
view.blit(gameover_text2, gameover_text2_rect)
#绘制重新开始按钮
again_rect.left, again_rect.top = \
(width - again_rect.width) // 2, \
gameover_text2_rect.bottom + 50
view.blit(again_image, again_rect)
#绘制结束游戏按钮
gameover_rect.left, gameover_rect.top = \
(width - again_rect.width) // 2, \
again_rect.bottom + 6
view.blit(gameover_image, gameover_rect)
# 检测用户的鼠标操作
# 如果用户按下鼠标左键
if pygame.mouse.get_pressed()[0]:
pos = pygame.mouse.get_pos()
if again_rect.left < pos[0] < again_rect.right and \
again_rect.top < pos[1] < again_rect.bottom:
tubeUp_array.clear()
tubeDown_array.clear()
main()
elif gameover_rect.left < pos[0] < gameover_rect.right and \
gameover_rect.top < pos[1] < gameover_rect.bottom:
pygame.quit()
sys.exit()
#更新界面
pygame.display.flip()
#延迟10毫秒
pygame.time.delay(10)
if __name__ == "__main__":
main()
main模块的总体集合代码如下
import pygame
import sys
import traceback
import Tubing
import random
import Bird
from pygame.locals import *
#初始化pygame 初始化pygame的扩音器
pygame.init()
pygame.mixer.init()
#定义存放管子的数组,tubeUp_array代表存放上面的那条管子,tubeDown_array代表存放下面的那条管子
tubeUp_array = []
tubeDown_array = []
#定义窗口的大小,也是背景图片的大小
bg_size = width,height = 903,495
#加载背景音乐、小鸟挥动翅膀的声音、小鸟撞击的声音
pygame.mixer.music.load("music/主界面BGM.mp3")
wings_sound = pygame.mixer.Sound("music/翅膀声.wav")
crash_sound = pygame.mixer.Sound("music/me_down.wav")
#设置分数显示的字体、游戏结束之后显示最高分数等字的字体
font = pygame.font.Font("font/font.ttf",35)
gameover_font = pygame.font.Font("font/font.ttf", 48)
#设置游戏窗口的大小和标题
view = pygame.display.set_mode(bg_size)
pygame.display.set_caption("像素鸟小游戏")
#加载背景图片、游戏结束后重新开始按键图片、游戏结束后退出游戏按键图片
background = pygame.image.load("image/BGM.png").convert()
again_image = pygame.image.load("image/again.png").convert_alpha()
gameover_image = pygame.image.load("image/gameover.png").convert_alpha()
def main():
#设置开始时间,用于每隔3秒就产生一对管子
start_time = 0
#设置游戏的分数
score = 0
#设置小鸟飞行挥动翅膀图片的切换flag
change = 1
#finish定义游戏是否结束(结束则为True),reader用于判断record.txt(存储最高分)的文件是否已经被打开(已经打开为True)
finish = False
reader = False
#获取重新开始按键的方框、获取结束游戏按键的方框
again_rect = again_image.get_rect()
gameover_rect = gameover_image.get_rect()
#创建一个小鸟对象
bird = Bird.Bird(bg_size)
#开始循环播放背景音乐
pygame.mixer.music.play(-1)
while True:
#如果点击了关闭事件就退出程序
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
#如果游戏还没结束
if finish == False:
#如果小鸟飞行超过上边界或者超过下边界,就播放撞击音乐,把finish置为True,表示程序结束
if bird.rect.top < 0 or bird.rect.bottom > width:
crash_sound.play()
finish = True
#绘制背景图片
view.blit(background,(0,0))
#切换小鸟飞行图片,以达到小鸟挥动翅膀的效果
if change % 3 == 0:
view.blit(bird.img1,bird.rect) #绘制小鸟图片
elif change % 3 == 1:
view.blit(bird.img2,bird.rect)
else:
view.blit(bird.img3,bird.rect)
change = change +1
#每3秒产生一对管子,一上一下,分别存放在ubeUp_array、tubeDown_array列表中
end_time = pygame.time.get_ticks()
if end_time - start_time >= 3000:
tubeDown = Tubing.Tubing_down()
tubeUp = Tubing.Tubing_up(tubeDown)
tubeDown_array.append(tubeDown)
tubeUp_array.append(tubeUp)
start_time = end_time
#判断小鸟精灵和管子精灵组有没有发生碰撞,如果发生了碰撞就播放碰撞音乐,同时将finish置为True
Up_crash = pygame.sprite.spritecollide(bird, tubeUp_array, False)
Down_crash = pygame.sprite.spritecollide(bird, tubeDown_array, False)
if Up_crash or Down_crash:
crash_sound.play()
finish = True
#小鸟每飞过一对管子分数就加1,显示管子和移动管子
for i in range(len(tubeDown_array)):
if bird.rect.right == tubeUp_array[i].rect.right:
score = score +1
view.blit(tubeDown_array[i].pipe,tubeDown_array[i].rect)
view.blit(tubeUp_array[i].pipe,tubeUp_array[i].rect)
tubeDown_array[i].move()
tubeUp_array[i].move()
#绘制分数
font_view = font.render('score = %s' %str(score),True,(255,255,255))
view.blit(font_view,(10,5))
#调用小鸟对象的下落方法
bird.drop()
#如果游戏已经结束,停止背景音乐的播放,绘制游戏结束后的界面
if finish == True:
pygame.mixer.music.stop()
#如果record.txt文件还未打开过,打开record.txt文件,将reader 置为 True
if not reader:
reader = True
# 读取历史最高分
with open("record.txt", "r") as f:
record_score = int(f.read())
#判断当前分数是否高于历史最高分,如果是,则写入record.txt文件中,同时将record_score赋值为最高分
if score > record_score:
with open("record.txt", "w") as f:
record_score = score
f.write(str(score))
#绘制游戏结束后的历史最高分
record_score_text = font.render("Best: %d" % record_score, True, (255,255,255))
view.blit(record_score_text, (50, 50))
#绘制游戏结束后的我的分数
gameover_text1 = gameover_font.render("Your Score: ", True, (255,255,255))
gameover_text1_rect = gameover_text1.get_rect()
gameover_text1_rect.left, gameover_text1_rect.top = \
(width - gameover_text1_rect.width) // 2, height // 3
view.blit(gameover_text1, gameover_text1_rect)
gameover_text2 = gameover_font.render(str(score), True, (255,255,255))
gameover_text2_rect = gameover_text2.get_rect()
gameover_text2_rect.left, gameover_text2_rect.top = \
(width - gameover_text2_rect.width) // 2, \
gameover_text1_rect.bottom + 10
view.blit(gameover_text2, gameover_text2_rect)
#绘制重新开始按钮
again_rect.left, again_rect.top = \
(width - again_rect.width) // 2, \
gameover_text2_rect.bottom + 50
view.blit(again_image, again_rect)
#绘制结束游戏按钮
gameover_rect.left, gameover_rect.top = \
(width - again_rect.width) // 2, \
again_rect.bottom + 6
view.blit(gameover_image, gameover_rect)
# 检测用户的鼠标操作
# 如果用户按下鼠标左键
if pygame.mouse.get_pressed()[0]:
pos = pygame.mouse.get_pos()
if again_rect.left < pos[0] < again_rect.right and \
again_rect.top < pos[1] < again_rect.bottom:
tubeUp_array.clear()
tubeDown_array.clear()
main()
elif gameover_rect.left < pos[0] < gameover_rect.right and \
gameover_rect.top < pos[1] < gameover_rect.bottom:
pygame.quit()
sys.exit()
#如果键盘上面的↑按键被按下,就调用小鸟对象的向上飞行的方法,同时播放小鸟挥动翅膀的声音
key_pressed = pygame.key.get_pressed()
if key_pressed[pygame.K_UP]:
wings_sound.play()
bird.fly()
#更新界面
pygame.display.flip()
#延迟10毫秒
pygame.time.delay(10)
if __name__ == "__main__":
main()
4、实验
实验运行结果如下:
5、总结和展望
不足之处:
1、没有实现小鸟向上飞行的时候,头朝上。往下坠落的时候,头往下
参考文献:
[1] 小甲鱼的飞机大战视频