#### 5.1 需求分析
- 记录分数
- 敌机被击中动画
- 玩家飞机爆炸动画
- 使用文件保存分数
#### 5.2 系统设计
##### 5.2.1 系统功能结构
为玩家飞机与敌机游戏元素包含的功能以及排行榜页面
彩图版飞机大战
- 敌机
- 绘制图片
- 移动敌机
- 敌机爆炸动画
- 玩家飞机
- 发射子弹
- 绘制图片
- 玩家爆炸动画
- 排行榜
- 保存分数到文件
- 更新文档分数排行
- 读取分数文件
- 显示文字
##### 5.2.2 系统业务流程
用户 -- 主窗体 -- 玩家飞机
-- 显示背景
-- 敌机
##### 5.2.3 系统预览
#### 5.3 系统开发必备
##### 5.3.1 开发工具准备
操作系统:win7,win10
开发工具:pycharm
python版本:python3.7
内置模块:sys, random, codecs
第三方模块:pygame
##### 5.3.2 文件夹组织架构
foo -- 项目文件夹
resources -- 资源文件夹
image --- 图片文件夹
main.py --- 程序文件夹
score.txt --- 分数文件
#### 5.4 彩图版飞机大战的实现
##### 5.4.1 主窗体的实现
开发流程和实现技术
开始 --> 初始化pygame游戏开发模块(应用:pygame.init()方法) ---> 设置游戏窗体大小(应用:pygame.display.set_mode()函数 --> 设置游戏窗体名称 (应用:pygame.display.set_caption()函数) ---> 设置游戏窗体图标(应用:pygame.display.set_icon()函数) --> 是否进入主循环 --> 是 --> 在主循环中更新屏幕(应用:pygame.display.update()方法) --> 游戏退出处理 (应用:pygame.quit()方法)
是否进入主循环 --> 否 --> 结束
具体步骤如下:
(1)创建名称为foo的文件夹,该文件夹用于保存飞机大战游戏的项目文件,并在该文件夹中创建resources文件夹用于保存项目资源,在resources文件夹中创建image用于保存游戏中所使用的图片资源。最后在foo项目文件夹中创建main.py文件,在该文件中编写实现飞机大战的游戏代码。
(2)导入pygame库与pygame中的常量库,然后定义窗体的宽度与高度,代码如下
```
import pygame # 导入pygame库
from pygame.locals import * # 导入pygame库中的一些常量
from sys import exit # 导入sys库中的exit函数
import random
import codecs
# 设置游戏屏幕大小
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 800
```
(3)接下来进行pygame的初始化工作,设置窗体的名称和图标,再创建窗体实例并设置窗体的大小以及背景色,最后通过循环实现窗体的显示与刷新,代码如下
```python
# 初始化pygame
pygame.init()
# 设置游戏界面大小
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 游戏界面标题
pygame.display.set_caption('彩图版飞机大战')
# 设置游戏界图标
ic_launcher = pygame.image.load('resources/image/ic_launcher.png').convert_alpha()
pygame.display.set_icon(ic_launcher)
# 背景图
background = pygame.image.load('resources/image/background.png').convert_alpha()
def startGame():
# 游戏循环帧率设置
clock = pygame.time.Clock()
# 判断游戏循环退出的参数
running = True
# 游戏主循环
while running:
# 绘制背景
screen.fill(0)
screen.blit(background,(0,0))
# 控制游戏最大帧率为60
clock.tick(60)
# 更新屏幕
pygame.display.update()
# 处理游戏退出
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
startGame()
```
##### 5.4.2 创建游戏精灵
本游戏元素包含玩家飞机、敌机及子弹。用户可以通过键盘移动玩家飞机在屏幕上的位置来打击不同位置的敌机。因此设计Player、Enemy和Bullet三个类对应的三种游戏精灵。对于Player需要的操作有射击和移动两种。移动又分上下左右四种情况。对于Enemy,则比较简单,只需要移动即可。从屏幕上方出现并移动到屏幕下方。对于Bullet,与飞机相同,仅需要以一定速度移动即可,根据游戏的元素实现创建游戏精灵功能,先要创建精灵的业务流程和实现技术。根据本功能实现技术,画出创建游戏精灵的业务流程
```python
# 子弹类
class Bullet(pygame.sprite.Sprite):
def __init__(self, bullet_img, init_pos):
# 调用父类的初始化方法初始化sprite的属性
pygame.sprite.Sprite.__init__(self)
self.image = bullet_img
self.rect = self.image.get_rect()
self.rect.midbottom = init_pos
self.speed = 10
def move(self):
self.rect.top -= self.speed
# 玩家飞机类
class Player(pygame.sprite.Sprite):
def __init__(self,player_rect,init_pos):
# 调用父类的初始化方法初始化sprite的属性
pygame.sprite.Sprite.__init__(self)
self.image = [] # 用来存储玩家飞机图片的列表
for i in range(len(player_rect)):
self.image.append(player_rect[i].convert_alpha())
self.rect = player_rect[0].get_rect() # 初始化图片所在的矩形
self.rect.topleft = init_pos # 初始化矩形的左上角坐标
self.speed = 8 # 初始化玩家飞机速度,这里是一个固定的值
self.bullets = pygame.sprite.Group() # 玩家飞机所发射的子弹的集合
self.img_index = 0 # 玩家飞机图片索引
self.is_hit = False # 玩家是否被击中
# 发射子弹
def shoot(self, bullet_img):
bullet = Bullet(bullet_img, self.rect.midtop)
self.bullets.add(bullet)
# 向上移动,需要判断边界
def moveUp(self):
if self.rect.top <= 0:
self.rect.top = 0
else:
self.rect.top -= self.speed
# 向下移动,需要判断边界
def moveDown(self):
if self.rect.top >= SCREEN_HEIGHT - self.rect.height:
self.rect.top = SCREEN_HEIGHT - self.rect.height
else:
self.rect.top += self.speed
# 向左移动,需要判断边界
def moveLeft(self):
if self.rect.left <= 0:
self.rect.left = 0
else:
self.rect.left -= self.speed
# 向右移动,需要判断边界
def moveRight(self):
if self.rect.left >= SCREEN_WIDTH - self.rect.width:
self.rect.left = SCREEN_WIDTH - self.rect.width
else:
self.redt.left += self.speed
# 敌机类
class Enemy(pygame.sprite.Sprite):
def __init__(self,enemy_img, enemy_down_imgs, init_pos):
# 调用父类的初始化方法初始化sprite的属性
pygame.sprite.Sprite.__init__(self)
self.image = enemy_img
self.rect = self.image.get_rect()
self.rect.topleft = init_pos
self.down_imgs = enemy_down_imgs
self.speed = 2
self.down_index = 0
# 敌机移动,边界判断及删除在游戏主循环里处理
def move(self):
self.rect.top += self.speed
```
##### 5.4.3 游戏核心逻辑
飞机移动和发射子弹,敌机的生成和移动,敌机和子弹、敌机和玩家飞机的碰撞检测,
开始 -- > 初始化游戏资源(应用:pygame.image.load()方法) -->初始化玩家飞机(应用:pygame.image.load() 方法) -- > 生成敌机(应用:pygame.image.load()方法)--- > 敌机玩家飞机碰撞检测(应用:pygame.sprite.collide_circle()方法)--> 绘制得分 (应用:pygame.font.Font()方法) --> 键盘按键处理(应用:pygame.key.get_pressed()方法) -- > 结束
具体的实现步骤如下
(1)引用各种图片资源方便后面使用
```python
# 游戏结束背景图片
game_over = pygame.image.load('resources/image/gameover.png')
# 子弹图片
plane_bullet = pygame.image.load('resources/image/bullet.png')
# 飞机图片
player_img1 = pygame.image.load('resources/image/player1.png')
player_img2 = pygame.image.load('resources/image/player2.png')
player_img3 = pygame.image.load('resources/image/player_off1.png')
player_img4 = pygame.image.load('resources/image/player_off2.png')
player_img5 = pygame.image.load('resources/image/player_off3.png')
# 敌机图片
enemy_img1 = pygame.image.load('resources/image/enemy1.png')
enemy_img2 = pygame.image.load('resources/image/enemy2.png')
enemy_img3 = pygame.image.load('resources/image/enemy3.png')
enemy_img4 = pygame.image.load('resources/image/enemy4.png')
```
(2) 在开始游戏的startGame()方法中的初始化玩家飞机,以及敌机,子弹图片资源和分数等资源,代码如下
```python
# 设置玩家飞机不同状态的图片列表,多张图片展示为动画效果
player_rect = []
# 玩家飞机图片
player_rect.append(player_img1)
player_rect.append(player_img2)
# 玩家爆炸图片
player_rect.append(player_img2)
player_rect.append(player_img3)
player_rect.append(player_img4)
player_rect.append(player_img5)
player_pos = [200,600]
# 初始化玩家飞机
player = Player(player_rect,player_pos)
# 子弹图片
bullet_img = plane_bullet
# 敌机不同状态的图片列表,多张图片展示为动画效果
enemy1_img = enemy_img1
enemy1_rect = enemy1_img.get_rect()
enemy1_down_imgs = []
enemy1_down_imgs.append(enemy_img1)
enemy1_down_imgs.append(enemy_img2)
enemy1_down_imgs.append(enemy_img3)
enemy1_down_imgs.append(enemy_img4)
# 储存敌机
enemies1 = pygame.sprite.Group()
# 存储被击毁的飞机,用来渲染击毁动画
enemies_down = pygame.sprite.Group()
# 初始化射击及敌机移动频率
shoot_frequency = 0
enemy_frequency = 0
# 玩家飞机被击中后的效果处理
player_down_index = 16
# 初始化分数
score = 0
```
(3)在开始游戏的startGame()方法的游戏主循环中,完成玩家飞机、敌机、子弹的逻辑处理和碰撞处理
```python
# 生成子弹,需要控制发射频率
# 首先判断玩家飞机没有被击中
if not player.is_hit:
if shoot_frequency % 15 == 0:
player.shoot(bullet_img)
shoot_frequency += 1
if shoot_frequency >= 15:
shoot_frequency = 0
for bullet in player.bullets:
# 以固定速度移动子弹
bullet.move()
# 移动出屏幕后删除子弹
if button.rect.bottom < 0:
player.bullets.remove(bullet)
# 显示子弹
player.bullets.draw(screen)
# 生成敌机,需要控制生成频率
if enemy_frequency % 50 == 0:
enemy1_pos = [random.randint(0,SCREEN_WIDTH - enemy1_rect.width),0]
enemy1 = Enemy(enemy1_img, enemy1_down_imgs, enemy1_pos)
enemies1 = Enemy(enemy1_img, enemy1_down_imgs, enemy1_pos)
# 绘制玩家飞机
if not player.is_hit:
screen.blit(player.image[player.img_index],player.rect)
# 更换图片索引使飞机有动画效果
player.img_index = shoot_frequency // 8
else:
# 玩家飞机被击中后的效果处理
player.img_index = player_down_index // 8
screen.blit(player.image[player.img_index], player.rect)
player_down_index += 1
if player_down_index > 47:
# 击中效果处理完成后游戏结束
running = False
# 敌机被子弹击中效果显示
for enemy_down in enemies_down:
if enemy_down.down_index == 0:
pass
if enemy_down.down_index > 7:
enemies_down.remove(enemy_down)
score += 100
continue
# 显示碰撞图片
screen.blit(enemy_down.down_imgs[enemy_down.down_index // 2], enemy_down.rect)
enemy_down.down_index += 1
# 显示精灵
enemies1.draw(screen)
# 绘制当前得分
score_font = pygame.font.Font(None, 36)
score_text = score_font.render(str(score), True, (255,255,255))
text_rect = score_text.get_rect()
text_rect.topleft = [10,10]
screen.blit(score_text, text_rect)
```
(4)处理玩家飞机移动,我们在键盘上按相应的按键后使玩家飞机进行上、下、左、右移动,需要在游戏主循环中进行处理,代码如下
```
# 获取键盘事件(上、下、左、右)
key_pressed = pygame.key.get_pressed()
# 处理键盘事件(移动飞机的位置)
if key_pressed[K_w] or key_pressed[K_UP]:
player.moveUp()
if key_pressed[K_s] or key_pressed[K_DOWN]:
player.moveDown()
if key_pressed[K_a] or key_pressed[K_LEFT]:
player.moveLeft()
if key_pressed[K_d] or key_pressed[K_RIGHT]:
player.moveRight()
```
##### 5.4.4 游戏排行榜
游戏结束后出现,记录了游戏前十最高分,
开始 -- > 绘制榜单背景(应用:pygame.display.set_mode()方法) --> 绘制排行榜标题文字(应用pygame.Font.SysFont()方法) --> 读取排行榜文档内容 (应用: 自定义read_txt()方法) ---> 绘制排行榜内容(应用: for循环) --> 结束
实现步骤:
(1)在foo项目文件中,创建score.txt文件用于保存用户分数,该文件可以直接导入或者自己创建,然后在该文件里面手动写入“0mr0mr0mr0mr0mr0mr0mr0mr0mr0”,其中mr为用于方便代码中进行处理的分割符
(2)创建完score.txt后,在main.py项目主文件中创建的writh_txt()、read_txt()方法,用于对score.txt文件进行写入与读取处理,代码如下
```python
'''
对文件的操作
写入文本:
传入参数为content,strim,path。content为需要写入的内容,数据类型为字符串;
path为写入的位置,数据类型为字符串,strim为写入方式
传入的path需如下定义:path = r'D:\text.txt'
strim = 'a'表示追加,写入TXT文件,可以换成'w',表示覆盖写入
'utf8表述写入的编码,可以换成’utf16'等。
'''
def write_txt(content,strim,path):
f = codecs.open(path,strim,'utf8')
f.write(str(content))
f.close()
'''
读取txt:
表示按行读取TXT文件,utf8表示读取编码为utf8的文件,可以根据需求改成utf16或者GBK等。
返回的为数组,每一个数组的元素代表一行,
若想返回字符串格式,可以将改写成return '\n'.join(lines)
'''
def read_txt(path):
with open(path, 'r', encoding = 'utf8') as f:
lines = f.readlines()
return lines
```
(3) 创建gameRanking()方法,用于显示排行榜页面,在其中读取分数文件,并把获取的分数显示到排行榜页面,代码如下
```
# 排行榜
def gameRanking():
screen2 = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
# 绘制背景
screen2.fill(0)
screen2.blit(background,(0,0))
# 使用系统字体
xtfont = pygame.font.SysFont('SimHei',30)
# 排行榜按钮
textstart = xtfont.render('排行榜',True,(255,0,0))
text_rect = textstart.get_rect()
text_rect.centerx = screen.get_rect().centerx
text_rect.centerx = 50
screen.blit(textstart,text_rect)
# 重新开始按钮
textstart = xtfont.render('重新开始 ', True, (255,0,0))
text_rect = textstart.get_rect()
text_rect.centerx = screen.get_rect().centerx
text_rect.centery = screen.get_rect().centery + 120
screen2.blit(textstart, text_rect)
# 获取排行榜文件内容
arrayscore = read.txt(r'score.txt')[0].split('mr')
# 遍历排行榜文件显示排行
for i in range(0, len(arrayscore)):
# 游戏Game Over 后显示最终得分
font = pygame.font.Font(None, 48)
# 排名从1到10
k = i + 1
text = font.render(str(k) + ' ' + arrayscore[i], True, (255,0,0))
text_rect = text.get_rect()
text_rect.conterx = screen2.get_rect().centerx
text_rect.contery = 80 + 30 * k
# 绘制分数内容
screen2.blit(text, text_rect)
```
#### 5.5 小结
掌握pygame游戏窗体的实现,熟练掌握如何创建精灵及检测精灵的碰撞