基于pygame实现的飞机大战游戏
目录
- 1.引言
- 1.1 背景
- 1.2 意义
- 1.3 功能
- 2.系统结构
- 2.1 整体框架
- 2.2 精灵与精灵组
- 2.3 功能介绍
- 2.3.1 玩家飞机
- 2.3.2 敌机类型和关卡设定
- 2.3.3 敌机登场
- 2.3.4 游戏道具和奖励
- 2.3.5 游戏结束
- 2.4 游戏模块
- 3.实现过程及代码
- 3.1 游戏框架搭建
- 3.1.1 游戏类的设计
- 3.1.2 搭建游戏框架
- 3.2 精灵与精灵组
- 3.2.1 图像的加载和绘制
- 3.2.2 游戏背景连续滚动
- 3.3 指示器面板
- 3.3.1 指示器面板类的设计
- 3.3.3 使用精灵实现文本标签
- 3.3.4 游戏数据修改
- 3.3.5 最好成绩的文件读写
- 3.3.6 游戏状态变化
- 3.3.7 游戏结束后的重置面板
- 3.4 逐帧动画的基本实现
- 3.4.1 派生飞机类
- 3.4.2 设置逐帧动画频率
- 3.5.1 派生敌机类
- 3.5.2 派生英雄飞机类
- 3.6 碰撞检测
- 3.6.1 创建碰撞检测
- 3.6.2 敌机撞毁英雄飞机
- 3.6.3 英雄飞机发射子弹
- 3.6.4 英雄飞机拾取道具
- 3.7 音乐与音效
- 3.7.1 加载和播放背景音乐
- 3.7.1 加载和播放音效
- 4.实验结果
- 5.总结和展望
- 3.1 游戏框架搭建
**摘要**
为了深入学习python程序设计语言面向对象设计的特点,以此开发一款桌面版的飞机大战游戏,学习python程序设计语言中数据与功能的组合。此项目是基本pygame开发的一款桌面版的飞机大战游戏。通过pygame中的精灵与精灵组的特性实现游戏面板上数据内容和图像内容的显示,实现飞机、子弹、道具与敌机的生成。帧数的设置实现游戏页面的显示和飞机的动态效果,监听游戏事件以实现游戏事件的执行,pygame中的collide模块实现飞机、子弹、道具和敌机之间的碰撞接触,mixer模块实现背景音乐和游戏音效。游戏有良好的界面显示,简单的游戏操作,给玩家良好的游戏体验。
1.引言
1.1 背景
此项目是基于pygame开发的一款飞机大战游戏,良好的游戏界面,玩家通过简单的键盘操作即可进行游戏,体验游戏乐趣。
1.2 意义
以pygame开发飞机大战游戏,体会和理解面向对象开发的同时,了解并学习pygame的高级使用
1.3 功能
2.系统结构
2.1 整体框架
2.2 精灵与精灵组
2.3 功能介绍
2.3.1 玩家飞机
名称 速度 飞行动画 被击中图片 被摧毁动画 摧毁音效 升级音效
英雄 5 有 无 有 有 有
2.3.2 敌机类型和关卡设定
(1)敌机类型
飞机大战游戏中一共有三种类型的敌机,各自对应的属性如表3所示:
(2)关卡设定
飞机大战游戏根据玩家的得分逐步提高难道,共设立3个关卡,具体的设定如表4所示:
2.3.3 敌机登场
2.3.4 游戏道具和奖励
英雄飞机每得到100000分会被奖励1次生命。此外,游戏开始每隔30秒会从游戏窗口上方随机位置向下飞出游戏道具,具体道具作用如表5所示:
序号 名称 功能描述 速度 播放音效
01 炸弹补给 英雄飞机拾取后,炸弹数量加1 5 有
02 子弹增强 英雄飞机拾取后,发射的子弹改为双排,持续时长20秒 5 有
2.3.5 游戏结束
2.4 游戏模块
3.实现过程及代码
3.1 游戏框架搭建
3.1.1 游戏类的设计
3.1.2 搭建游戏框架
import pygame定义全局变量
SCREEN_RECT = pygame.Rect(0, 0, 480, 700) # 游戏窗口矩形
```python
import pygame
from game_hud import *
from game_items import *
from game_music import *class Game(object):"""游戏核心类"""def __init__(self):# 游戏窗口self.main_window = pygame.display.set_mode(SCREEN_RECT.size)pygame.display.set_caption('飞机大战')#游戏状态self.is_game_over = False # 结束标记self.is_game_pause = False # 暂停标记def reset_game(self):"""重置游戏数据"""self.is_game_over = Falseself.is_game_pause = Falsedef start(self):"""开启游戏主逻辑"""# 创建时钟clock = pygame.time.Clock()while True:# 处理事件监听if self.event_handler():# event.handler 返回True则说明发生了退出事件return# 根据游戏转态切换界面显示的内容if self.is_game_over:print('游戏已经结束,按空格键重新开始游戏...')elif self.is_game_pause:print('游戏已经暂停,按空格键继续...')else:print('游戏进行中...') #刷新页面pygame.display.update()# 设置刷新率
clock.tick(60)def event_handler(self):"""获取并处理事件"""for event in pygame.event.get():if event.type == pygame.QUIT:# 退出按钮被点击return Trueelif event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:# 用户按下了键盘上的 ESC 键return Trueelif event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE:# 用户按下了键盘上的 空格 键if self.is_game_over:# 游戏已经结束,重置游戏self.reset_game()else:# 游戏还没结束,切换暂停状态self.is_game_pause = not self.is_game_pausereturn False
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
if __name__ == '__main__':#初始化数据pygame.init()#开始游戏Game().start()#释放游戏资源pygame.quit()
3.2 精灵与精灵组
3.2.1 图像的加载和绘制
#定义精灵类
class GameSprite(pygame.sprite.Sprite):res_path = './res/images/'def __init__(self, image_name, speed, *group):"""初始化精灵对象"""# 调用父类方法,把当前精灵对象放到精灵组里super(GameSprite, self).__init__(*group)# 创建图片self.image = pygame.image.load(self.res_path + image_name)# 获取矩形self.rect = self.image.get_rect()# 设置移动速度self.speed = speeddef update(self, *args):"""更新元素数据"""self.rect.y += self.speed
class Game(object):"""游戏核心类"""def __init__(self):···# 游戏精灵组self.all_group = pygame.sprite.Group() # 存放界面上的所有精灵self.enemies_group = pygame.sprite.Group() # 敌机精灵组self.supplies_group = pygame.sprite.Group() # 道具精灵组# 游戏精灵GameSprite('background.png', 1, self.all_group) # 创建背景精灵hero_sprite = GameSprite('me1.png', 0, self)hero_sprite.rect.center = SCREEN_RECT.centerdef start(self):….# 绘制内容self.all_group.draw(self.main_window)
def start(self):…while True:…else:print('游戏进行中...')self.all_group.update()
3.2.2 游戏背景连续滚动
(1)基本构思
游戏在运行的时候,整个游戏窗口显示一张以星空为背景的图像,同时背景图像会缓缓地向下方移动,给玩家产生一种英雄飞机向上方飞行的错觉
背景图像连续、不间断地向下运动的实现,如示意图3:
class Background(GameSprite):def __init__(self, is_alt, *group):"""如果 is_alt 为True则在初始化时这个精灵显示在窗口上方,False则显示在窗口内部"""super(Background, self).__init__('background.png', 1, *group)if is_alt:self.rect.y = -self.rect.hdef update(self, *args):super(Background, self).update(*args)# 如果图片已经滚动到底部,则立即回到窗口的最上面,供重新显示if self.rect.y > self.rect.h:self.rect.y = -self.rect.y
class Game(object):"""游戏核心类"""def __init__(self):…# 创建背景精灵self.all_group.add(Background(False), Background(True))
3.3 指示器面板
在飞机大战游戏中设计一个指示器面板,负责显示和游戏状态以及数据相关的内容,包括:状态图像、游戏得分、炸弹图像、炸弹数量、英雄命数指示,以及在游戏暂停或结束时,显示在游戏窗口中央位置的提示信息等。
3.3.1 指示器面板类的设计
"""游戏控制面板/提示信息模块"""
import pygame
class HUDPanel(object):"""所有面板精灵的控制类"""def __init__(self, display_group):# 基本数据self.score = 0 # 游戏得分self.lives_count = 3 # 生命计数self.level = 1 # 游戏级别self.best_score = 0 # 最好成绩
class StatusButton(GameSprite):"""状态按钮精灵类"""def __init__(self, image_names, *groups):"""image_names 接受一个元组,元组的0下标必须是暂停的图片,1下标必须是运行的图片"""super(StatusButton, self).__init__(image_names[0], 0, *groups)# 准备用于切换显示的两张图片self.images = [pygame.image.load(self.res_path + name) for name in image_names]def switch_status(self, is_pause):"""根据是否暂停,切换要使用的图片对象"""self.image = self.images[1 if is_pause else 0]
class HUDPanel(object):"""所有面板精灵的控制类"""margin = 10 # 精灵之间的间距white = (255, 255, 255) # 白色gray = (64, 64, 64) # 灰色
(2)在初始化方法的末尾增加以下代码,创建精灵并设置显示位置:
def __init__(self, display_group):# 图像精灵# 状态精灵self.status_sprite = StatusButton(('pause_nor.png', 'resume_nor.png'), display_group)self.status_sprite.rect.top = self.marginself.status_sprite.rect.left = self.margin# 炸弹精灵self.bomb_sprite = GameSprite('bomb.png', 0, display_group)self.bomb_sprite.rect.x = self.marginself.bomb_sprite.rect.bottom = SCREEN_RECT.bottom - self.margin# 生命精灵self.lives_sprite = GameSprite('life.png', 0, display_group)self.lives_sprite.rect.right = self.lives_label.rect.left - self.marginself.lives_sprite.rect.bottom = SCREEN_RECT.bottom - self.margin
(3)在game.py的初始化方法中添加创建游戏控制面板的代码:
class Game(object):"""游戏核心类"""def __init__(self):···# 创建游戏控制面板self.hud_panel = HUDPanel(self.all_group)
3.3.3 使用精灵实现文本标签
class Label(pygame.sprite.Sprite):"""标签精灵类"""font_path = './res/font/MarkerFelt.ttc'def __init__(self, text, size, color, *groups):"""初始化标签精灵的数据"""super(Label, self).__init__(*groups)# 创建字体对象self.font = pygame.font.Font(self.font_path, size)# 字体颜色self.color = color# 精灵属性self.image = self.font.render(text, True, self.color)self.rect = self.image.get_rect()def set_text(self, text):"""更新显示的文本内容"""self.image = self.font.render(text, True, self.color)self.rect = self.image.get_rect()
(2)创建指示器面板中的文本标签精灵
在game_hud.py中的HUDPanel类初始化方法的末尾增加代码,创建指示器面板中的文本标签精灵,代码如下:
#得分标签self.score_label = Label('%d' % self.score, 32, self.gray, display_group)self.score_label.rect.midleft = (self.status_sprite.rect.right + self.margin,self.status_sprite.rect.centery)# 炸弹计数标签self.bomb_label = Label('X 3', 32, self.gray, display_group)self.bomb_label.rect.midleft = (self.bomb_sprite.rect.right + self.margin,self.bomb_sprite.rect.centery)# 生命计数标签self.lives_label = Label('X %d' % self.lives_count, 32, self.gray, display_group)self.lives_label.rect.midright = (SCREEN_RECT.right - self.margin,self.bomb_sprite.rect.centery)# 最好成绩标签self.best_label = Label('Best:%d' % self.best_score, 36, self.white)self.best_label.rect.center = SCREEN_RECT.center# 状态标签self.status_label = Label('Game Paused!', 48, self.white)self.status_label.rect.midbottom = (self.best_label.rect.centerx,self.best_label.rect.y - 2 * self.margin)# 提示标签self.tip_label = Label('Press spaceBar to continue', 22, self.white)self.tip_label.rect.midtop = (self.best_label.rect.centerx,self.best_label.rect.bottom + 8 * self.margin)
3.3.4 游戏数据修改
def show_bomb(self, count):"""修改炸弹数量 X count """self.bomb_label.set_text('X %d' % count)self.bomb_label.rect.midleft = (self.bomb_sprite.rect.right + self.margin,self.bomb_sprite.rect.centery)
在game.py中event_handler方法中添加使用炸弹事件
def event_handler(self):"""获取并处理事件"""for event in pygame.event.get():···# 必须在游戏结束也没暂停才能执行的操作if not self.is_game_over and not self.is_game_pause:if event.type == pygame.KEYDOWN and event.key == pygame.K_b: self.hud_panel.show_bomb(self.hero_sprite.bomb_count)
def show_lives(self):"""显示最新的生命计数为 X lives_count"""self.lives_label.set_text('X %d' % self.lives_count)# 修改生命计数精灵的位置self.lives_label.rect.midright = (SCREEN_RECT.right - self.margin,self.bomb_sprite.rect.centery)# 修改生命精灵的位置self.lives_sprite.rect.right = self.lives_label.rect.x - self.margin
reward_score = 100000 # 奖励分值level2_score = 10000 # 级别2分值level3_score = 50000 # 级别3分值
在HUDPanel类中定义并实现increase_score方法,代码如下:
def increase_score(self, enemy_score):"""增加得分,注意同时要处理增加生命、关卡升级、更新最好成绩"""# 计算最新得分score = self.score + enemy_score# 判断是否增加生命if score // self.reward_score != self.score // self.reward_score:self.lives_count += 1self.show_lives()self.score = score# 更新最好成绩self.best_score = score if score > self.best_score else self.best_score# 计算最新关卡等级if score < self.level2_score:level = 1elif score < self.level3_score:level = 2else:level = 3is_upgrade = level != self.levelself.level = level# 更新得分的精灵显示内容self.score_label.set_text('%d' % score)self.score_label.rect.midleft = (self.status_sprite.rect.right + self.margin,self.status_sprite.rect.centery)# 返回是否升级给游戏主逻辑return is_upgrade
3.3.5 最好成绩的文件读写
record_filename = "record.txt" # 最好成绩文件名
(2)在HUDPanel类中定义save_best_score方法:代码如下:
def save_best_score(self):"""保存当前最好得分到文件里"""file = open(self.record_filename, 'w')file.write('%d' % self.best_score)file.close()
在game.py中的start方法中添加退出游戏之前保存最好成绩事件
while True:# 判断英雄是否已经死亡self.is_game_over = self.hud_panel.lives_count == 0# 处理事件监听if self.event_handler():# event.handler 返回True则说明发生了退出事件# 退出游戏之前要保存最好的成绩self.hud_panel.save_best_score()return
(3)在HUDPanel类中定义load_best_score方法:代码如下
def load_best_score(self):"""从文件里重新加载最好得分"""try:# 读取文件内容file = open(self.record_filename, 'r')content = file.read()file.close()# 转换内容为数字self.best_score = int(content)except (FileNotFoundError, ValueError):print('读取最高得分文件,发生异常')
在HUDPanel类中的初始化方法末尾添加加载最好成绩:代码如下
# 从文件里加载最好成绩self.load_best_score()print('初始化控制面板,当前最好得分是:', self.best_score)
3.3.6 游戏状态变化
# 游戏精灵# 创建背景精灵self.all_group.add(Background(False), Background(True))# 创建游戏控制面板self.hud_panel = HUDPanel(self.all_group)# 创建英雄飞机精灵hero = GameSprite('me1.png', 0, self.all_group)hero.rect.center = SCREEN_RECT.center # 显示在屏幕中央
def panel_paused(self, is_game_over, display_group):"""游戏停止,显示提示信息。is_game_over为 True 则说明游戏结束,为 False 则说明游戏暂停"""# 判断是否已经显示了提示信息if display_group.has(self.best_label, self.status_label, self.tip_label):return# 根据游戏状态生成提示文本status = 'Game Over!' if is_game_over else 'Game Paused!'tip = 'Press spaceBar to 'tip += 'play again.' if is_game_over else 'continue.'# 修改标签精灵的文本内容self.best_label.set_text('Best:%d' % self.best_score)self.status_label.set_text(status)self.tip_label.set_text(tip)# 修正标签精灵的位置self.best_label.rect.center = SCREEN_RECT.centerself.status_label.rect.midbottom = (self.best_label.rect.centerx,self.best_label.rect.y - 2 * self.margin)self.tip_label.rect.midtop = (self.best_label.rect.centerx,self.best_label.rect.bottom + 8 * self.margin)# 把标签精灵添加到精灵组display_group.add(self.best_label, self.status_label, self.tip_label)# 修改状态按钮self.status_sprite.switch_status(True)
(2)在game_hud.py中的HUDPanel类中定义panel_resume方法,代码如下:
def panel_resume(self, display_group):"""取消停止状态,隐藏提示信息"""display_group.remove(self.best_label, self.status_label, self.tip_label)# 修改状态按钮self.status_sprite.switch_status(False)
def start(self):···while True:···# 根据游戏转态切换界面显示的内容if self.is_game_over:# print('游戏已经结束,按空格键重新开始游戏...')self.hud_panel.panel_paused(True, self.all_group)elif self.is_game_pause:# print('游戏已经暂停,按空格键继续...')self.hud_panel.panel_paused(False, self.all_group)else:# print('游戏进行中...')# self.all_group.update()self.hud_panel.panel_resume(self.all_group)
3.3.7 游戏结束后的重置面板
(1)在game.py中game循环中添加判断游戏是否结束的代码,如下:
while True:# 判断英雄是否已经死亡self.is_game_over = self.hud_panel.lives_count == 0# 处理事件监听if self.event_handler():# event.handler 返回True则说明发生了退出事件# 退出游戏之前要保存最好的成绩self.hud_panel.save_best_score()return
(2)在game_hud.py中的HUDPanel类中定义reset_panel方法,代码如下:
def reset_panel(self):"""重置面板数据"""# 重置数据self.score = 0self.lives_count = 3# 重置精灵数据self.increase_score(0)self.show_bomb(3)self.show_lives()
(3)在game.py的reset_game重新游戏方法中调用,代码如下:
def reset_game(self):"""重置游戏数据"""self.is_game_over = Falseself.is_game_pause = False# 重置面板self.hud_panel.reset_panel()
3.4 逐帧动画的基本实现
3.4.1 派生飞机类
在game_items.py模块中从GameSprite类中派生一个Plane类,代码如下:
class Plane(GameSprite):"""飞机精灵类"""def __init__(self, normal_names, speed, hp, value, wav_name, hurt_name, destroy_names, *group):"""飞机类的初始化"""super(Plane, self).__init__(normal_names[0], speed, *group)# 飞机基本属性self.hp = hp # 当前生命值self.max_hp = hp # 初始生命值self.value = value # 分值self.wav_name = wav_name # 音效名# 正常状态图像列表self.normal_images = [pygame.image.load(self.res_path + name) for name in normal_names]self.normal_index = 0 # 正常状态图像索引self.hurt_image = pygame.image.load(self.res_path + hurt_name) # 受伤图像# 摧毁状态图像列表self.destroy_images = [pygame.image.load(self.res_path + name) for name in destroy_names]self.destroy_index = 0 # 摧毁状态索引def reset_plane(self):"""重置飞机"""self.hp = self.max_hpself.normal_index = 0self.destroy_index = 0self.image = self.normal_images[0]def update(self, *args):"""更新状态,准备下一次要显示的内容"""# 判断是否要更新if not args[0]:returnif self.hp == self.max_hp:# 切换要显示的图片self.image = self.normal_images[self.normal_index]# 计算下一次显示的索引count = len(self.normal_images)self.normal_index = (self.normal_index + 1) % countelif self.hp > 0:# 受伤self.image = self.hurt_imageelse:# 死亡if self.destroy_index < len(self.destroy_images):self.image = self.destroy_images[self.destroy_index]self.destroy_index += 1else:self.reset_plane()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
3.4.2 设置逐帧动画频率
FRAME_INTERVAL = 10 # 逐帧动画间隔帧数
def start(self):"""开启游戏主逻辑"""# 创建时钟clock = pygame.time.Clock()# 动画帧数计数器frame_count = 0while True:···else:# print('游戏进行中...')self.hud_panel.panel_resume(self.all_group) frame_count = (frame_count + 1) % FRAME_INTERVALself.all_group.update(frame_count == 0)
3.5.1 派生敌机类
class Enemy(Plane):"""敌人飞机"""def __init__(self, kind, max_speed, *groups):"""初始化敌人飞机"""self.kind = kindself.max_speed = max_speedif kind == 0:# 小敌机super(Enemy, self).__init__(['enemy1.png'], 1, 1, 1000,'enemy1_down.wav', 'enemy1.png',['enemy1_down%d.png' % i for i in range(1, 5)],*groups)elif kind == 1:# 中敌机super(Enemy, self).__init__(['enemy2.png'], 1, 6, 6000,'enemy2_down.wav', 'enemy2_hit.png',['enemy2_down%d.png' % i for i in range(1, 5)],*groups)elif kind == 2:# 大敌机super(Enemy, self).__init__(['enemy3_n1.png', 'enemy3_n2.png'], 1, 15, 15000,'enemy3_down.wav', 'enemy3_hit.png',['enemy3_down%d.png' % i for i in range(1, 7)],*groups)# 初始化飞机时,要让飞机随机的选择一个位置显示self.reset_plane()def reset_plane(self):"""重置敌人飞机"""super(Enemy, self).reset_plane()
def create_enemies(self):"""创建敌机"""count = len(self.enemies_group.sprites())groups = (self.all_group, self.enemies_group)# 根据不同的关卡创建不同数量的敌机if self.hud_panel.level == 1 and count == 0:# 关卡1for i in range(16):Enemy(0, 3, *groups)elif self.hud_panel.level == 2 and count == 16:# 关卡2for enemy in self.enemies_group.sprites():enemy.max_speed = 5for i in range(8):Enemy(0, 5, *groups)for i in range(2):Enemy(1, 1, *groups)elif self.hud_panel.level == 3 and count == 26:# 关卡3for enemy in self.enemies_group.sprites():enemy.max_speed = 7 if enemy.kind == 0 else 3for i in range(8):Enemy(0, 7, *groups)for i in range(2):Enemy(1, 3, *groups)for i in range(2):Enemy(2, 1, *groups)
# 初始化敌机self.create_enemies()
def reset_plane(self):"""重置敌人飞机"""super(Enemy, self).reset_plane()# 敌人飞机的数据重置x = random.randint(0, SCREEN_RECT.w - self.rect.w)y = random.randint(0, SCREEN_RECT.h - self.rect.h) - SCREEN_RECT.hself.rect.topleft = (x, y)# 重置飞机的飞行速度self.speed = random.randint(1, self.max_speed)
def update(self, *args):"""更新飞机的位置信息"""super(Enemy, self).update(*args)# 根据血量判断是否还要移动if self.hp > 0:self.rect.y += self.speed# 如果移动后已经到达屏幕外,则需要重置飞机if self.rect.y >= SCREEN_RECT.h:self.reset_plane()
3.5.2 派生英雄飞机类
HERO_BOMB_COUNT = 3 # 英雄默认炸弹数量
HERO_DEFAULT_MID_BOTTOM = (SCREEN_RECT.centerx,SCREEN_RECT.bottom - 90) # 英雄默认初始位置
(2)在game_items.py模块中从Plane类派生一个Hero类,代码如下:
class Hero(Plane):"""英雄飞机"""def __init__(self, *groups):"""初始化英雄飞机"""self.is_power = False # 是否无敌self.bomb_count = HERO_BOMB_COUNT # 炸弹数量self.bullets_kind = 0 # 子弹类型self.bullets_group = pygame.sprite.Group() # 子弹精灵组super(Hero, self).__init__(('me1.png', 'me2.png'),5, 1, 0, 'me_down.wav', 'me1.png',['me_destroy_%d.png' % x for x in range(1, 5)],*groups)self.rect.midbottom = HERO_DEFAULT_MID_BOTTOM # 创建好飞机之后要设置飞机位置为屏幕底部中间
(3)在game.py模块中的初始化方法修改创建英雄飞机精灵和修改显示炸弹数量
# 创建英雄飞机精灵self.hero_sprite = Hero(self.all_group)self.hud_panel.show_bomb(self.hero_sprite.bomb_count)
按下右键 按下左键 右键-左键 移动方向
0 0 0 不移动
1 0 1 向右
0 -1 -1 向左
1 1 0 不移动
按下上键 按下下键 上键-下键 移动方向
0 0 0 不移动
1 0 1 向上
0 -1 -1 向下
1 1 0 不移动
···while True:···else:# print('游戏进行中...')self.hud_panel.panel_resume(self.all_group)# 处理长按事件keys = pygame.key.get_pressed() # get_pressed()得到一个元组,每一个按键在元组里都有一个下标,对应下标值为1则说明按键被按下move_hor = keys[pygame.K_RIGHT] - keys[pygame.K_LEFT] # 水平的移动基数move_ver = keys[pygame.K_DOWN] - keys[pygame.K_UP] # 垂直的移动基数# 检测碰撞self.check_collide()frame_count = (frame_count + 1) % FRAME_INTERVALself.all_group.update(frame_count == 0, move_hor, move_ver)
(2)在game_items.py模块中英雄飞机类增加update方法,代码如下:
def update(self, *args):"""*args的0号下标说明是否要更新下一帧动画,1号说明玩家飞机水平方向移动基数,2号说明玩家飞机垂直方向移动基数"""super(Hero, self).update(*args)if len(args) != 3 or self.hp <= 0:return# 屏幕边缘的位置修正self.rect.x += args[1] * self.speedself.rect.x = 0 if self.rect.x < 0 else self.rect.xif self.rect.right > SCREEN_RECT.right:self.rect.right = SCREEN_RECT.rightself.rect.y += args[2] * self.speedself.rect.y = 0 if self.rect.y < 0 else self.rect.yif self.rect.bottom > SCREEN_RECT.bottom:self.rect.bottom = SCREEN_RECT.bottom
def blowup(self, enemies_group):"""炸毁所有敌机,并返回得到的总分值"""# 判断是否能够发起引爆if self.bomb_count <= 0 or self.hp <= 0:return 0# 引爆所有敌机,累计得分self.bomb_count -= 1score = 0count = 0for enemy in enemies_group.sprites():if enemy.rect.bottom > 0:score += enemy.valueenemy.hp = 0count += 1print('炸毁了%d架敌机,获取得分%d' % (count, score))return score
def event_handler(self):"""获取并处理事件"""for event in pygame.event.get():···# 必须在游戏结束也没暂停才能执行的操作if not self.is_game_over and not self.is_game_pause:if event.type == pygame.KEYDOWN and event.key == pygame.K_b:# 释放一颗炸弹,并修改炸弹数量score = self.hero_sprite.blowup(self.enemies_group)self.hud_panel.show_bomb(self.hero_sprite.bomb_count)if self.hud_panel.increase_score(score):self.create_enemies()
3.6 碰撞检测
3.6.1 创建碰撞检测
(1)在game.py中创建check_collide方法,代码如下:
def check_collide(self):"""检查是否有碰撞"""if not self.hero_sprite.is_power:collide_enemies = pygame.sprite.spritecollide(self.hero_sprite, self.enemies_group,False, pygame.sprite.collide_mask)
(2)在game_items.py模块中的GameSprite类的初始化方法中生成描边,代码如下:
# 生成遮罩属性,提高碰撞检测的执行效率self.mask = pygame.mask.from_surface(self.image)
def start(self):···while True:···else:# print('游戏进行中...')self.hud_panel.panel_resume(self.all_group) # 检测碰撞self.check_collide()
3.6.2 敌机撞毁英雄飞机
def check_collide(self):"""检查是否有碰撞"""if not self.hero_sprite.is_power:collide_enemies = pygame.sprite.spritecollide(self.hero_sprite, self.enemies_group,False, pygame.sprite.collide_mask)# 过滤掉已经被摧毁的敌机collide_enemies = list(filter(lambda x: x.hp > 0, collide_enemies))# 撞毁玩家飞机if collide_enemies:self.hero_sprite.hp = 0# 撞毁敌人飞机for enemy in collide_enemies:enemy.hp = 0
HERO_DEAD_EVENT = pygame.USEREVENT # 英雄牺牲事件
HERO_POWER_OFF_EVENT = pygame.USEREVENT + 1 # 取消英雄无敌事件
(2)重新Hero类中的reset_plane方法,代码如下:
def reset_plane(self):"""重置玩家飞机数据"""super(Hero, self).reset_plane()self.is_power = Trueself.bomb_count = HERO_BOMB_COUNTself.bullets_kind = 0# 发布事件,让游戏主逻辑更新界面pygame.event.post(pygame.event.Event(HERO_DEAD_EVENT))# 发布定时事件pygame.time.set_timer(HERO_POWER_OFF_EVENT, 3000)
(3)在game.py模块的event_handler方法中,当游戏未结束也没暂停时判断事件,代码如下:
for event in pygame.event.get():···# 必须在游戏未结束也没暂停才能执行的操作if not self.is_game_over and not self.is_game_pause: ··· elif event.type == HERO_DEAD_EVENT:# 玩家飞机死亡self.hud_panel.lives_count -= 1self.hud_panel.show_lives()self.hud_panel.show_bomb(self.hero_sprite.bomb_count)elif event.type == HERO_POWER_OFF_EVENT:self.hero_sprite.is_power = Falsepygame.time.set_timer(HERO_POWER_OFF_EVENT, 0) # 设置定时器延时为0,可以取消定时器
玩家飞机被摧毁,重置英雄飞机位置,在game.py模块的reset_game方法中,代码如下:
def reset_game(self):# 重置英雄飞机位置self.hero_sprite.rect.midbottom = HERO_DEFAULT_MID_BOTTOM
3.6.3 英雄飞机发射子弹
class Bullet(GameSprite):"""子弹类"""def __init__(self, kind, *group):"""初始化子弹数据"""image_name = 'bullet1.png' if kind == 0 else 'bullet2.png'super(Bullet, self).__init__(image_name, -12, *group)self.damage = 1 # 杀伤力def update(self, *args):"""更新子弹的数据"""super(Bullet, self).update(*args)# 飞出屏幕之外则需要销毁子弹if self.rect.bottom < 0:self.kill()
HERO_FIRE_EVENT = pygame.USEREVENT + 2 # 英雄发射子弹事件
(2)在Hero类的初始化方法中的末尾,设置英雄发射子弹的定时器事件,代码如下:
def __init__(self, *groups):pygame.time.set_timer(HERO_FIRE_EVENT, 200) # 创建玩家飞机之后每0.2秒激活一次发射子弹事件
(3)在Hero类中定义fire方法并根据bullet_kind属性发射子弹,子弹之间间距为15
def fire(self, display_group):""""英雄飞机发射一轮新的子弹"""# 准备子弹要显示到的组groups = (display_group, self.bullets_group)# 创建新子弹并定位for i in range(3):bullet1 = Bullet(self.bullets_kind, *groups)y = self.rect.y - i * 15# 判断子弹类型if self.bullets_kind == 0:bullet1.rect.midbottom = (self.rect.centerx, y)else:bullet1.rect.midbottom = (self.rect.centerx - 20, y)# 再创建一颗子弹bullet2 = Bullet(self.bullets_kind, *groups)bullet2.rect.midbottom = (self.rect.centerx + 20, y)
(4)在game.py模块的event_handler方法中监听到英雄飞机发射子弹定时器事件后,让英雄飞机调用发射子弹方法
# 必须在游戏未结束也没暂停才能执行的操作
if not self.is_game_over and not self.is_game_pause:
···
elif event.type == HERO_FIRE_EVENT:# 英雄飞机发射子弹定时事件self.hero_sprite.fire(self.all_group)
图6 子弹碰撞敌机流程图
(1)在Game类check_collide方法的末尾增加子弹击中敌机的处理,代码如下:
# 子弹和敌机的碰撞分析hit_enemies=pygame.sprite.groupcollide(self.enemies_group, self.hero_sprite.bullets_group, False, False, pygame.sprite.collide_mask)for enemy in hit_enemies:# 已经被摧毁的飞机不需要再处理if enemy.hp <= 0:continuefor bullet in hit_enemies[enemy]:bullet.kill() # 销毁子弹enemy.hp -= bullet.damage # 修改敌机生命值if enemy.hp > 0:continue # 如果敌机没有被摧毁,则继续遍历下一个子弹# 当前这一颗子弹已经把敌机摧毁if self.hud_panel.increase_score(enemy.value):self.create_enemies()# 这个飞机已经被摧毁,不需要再遍历下一颗子弹了break
(2)游戏的重置,销毁敌机和子弹,在Game类的reset_game方法中末尾重置,代码如下:
# 销毁所有的敌人飞机for enemy in self.enemies_group:enemy.kill()# 销毁所有子弹for bullet in self.hero_sprite.bullets_group:bullet.kill()# 重新创建飞机self.create_enemies()
3.6.4 英雄飞机拾取道具
游戏过程中,每隔30秒会从游戏窗口上方随机位置向下飞出游戏道具
class Supply(GameSprite):"""道具精灵"""def __init__(self, kind, *group):"""初始化道具属性"""image_name = 'bomb_supply.png' if kind == 0 else 'bullet_supply.png'super(Supply, self).__init__(image_name, 5, *group)self.kind = kind # 道具类型self.wav_name = 'get_bomb.wav' if kind == 0 else 'get_bullet.wav' # 道具的音效self.rect.bottom = SCREEN_RECT.h # 道具的初始位置def update(self, *args):"""修改道具位置"""if self.rect.y > SCREEN_RECT.h: # 如果已经移动到屏幕之外则不需要继续移动returnsuper(Supply, self).update(*args)def throw_supply(self):"""投放道具"""self.rect.bottom = 0 # 移动道具到窗口顶部self.rect.x = random.randint(0, SCREEN_RECT.w - self.rect.w)
THROW_SUPPLY_EVENT = pygame.USEREVENT + 3 # 投放道具事件
(2)在game.py的Game类中实现create_supplies方法
def create_supply(self):"""初始化两个道具,并开启投放道具的定时器"""Supply(0, self.all_group, self.supplies_group)Supply(1, self.all_group, self.supplies_group)pygame.time.set_timer(THROW_SUPPLY_EVENT, 30000)
# 初始化道具
self.create_supply()
(3)在事件监听中监听到投放道具定时器事件后,让随机道具调用投放道具方法
elif event.type == THROW_SUPPLY_EVENT:# 随机投放一个道具supply = random.choice(self.supplies_group.sprites())supply.throw_supply()
BULLET_ENHANCED_OFF_EVENT = pygame.USEREVENT + 4 # 关闭子弹增强事件
(2)在Game类check_collide方法的末尾增加英雄飞机拾取道具的处理,代码如下:
# 检查英雄飞机和道具的碰撞supplies = pygame.sprite.spritecollide(self.hero_sprite, self.supplies_group,False, pygame.sprite.collide_mask)if supplies:supply = supplies[0]self.player.play_sound(supply.wav_name) # 根据道具类型播放捡道具的音效# 根据道具类型产生不同的行为if supply.kind == 0:self.hero_sprite.bomb_count += 1self.hud_panel.show_bomb(self.hero_sprite.bomb_count)else:self.hero_sprite.bullets_kind = 1 # 修改英雄子弹为双排子弹pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 20000) # 20秒之后重新变为单排子弹# 移动道具到屏幕之下supply.rect.y = SCREEN_RECT.h
(3)在event_handler方法中监听到关闭子弹增强的定时器事件
elif event.type == BULLET_ENHANCED_OFF_EVENT:# 玩家使用双排子弹的时间已经结束,需要恢复为单排子弹self.hero_sprite.bullets_kind = 0 # 恢复为单排子弹pygame.time.set_timer(BULLET_ENHANCED_OFF_EVENT, 0) # 取消定时
3.7 音乐与音效
3.7.1 加载和播放背景音乐
(1)在game_music模块中定义MusicPlayer类,代码如下:
"""游戏音乐控制模块"""
import pygame
import osclass MusicPlayer(object):"""音乐播放类"""res_path = './res/sound/'def __init__(self, music_file):"""初始化音乐播放器"""# 加载背景音乐pygame.mixer.music.load(self.res_path + music_file)pygame.mixer.music.set_volume(0.1)# 初始化音效的字典self.sound_dict = {}files = os.listdir(self.res_path)for file in files:if file == music_file: # 背景音乐不需要处理continuesound = pygame.mixer.Sound(self.res_path + file)self.sound_dict[file] = sound@staticmethoddef play_music():"""播放背景音乐"""pygame.mixer.music.play(-1)@staticmethoddef pause_music(is_pause):"""根据暂停状态决定是否要播放背景音乐"""if is_pause:pygame.mixer.music.pause()else:pygame.mixer.music.unpause()def play_sound(self, wav_name):"""根据文件名,播放音效"""self.sound_dict[wav_name].play()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
(2)在Game类初始化方法中调用音乐类的背景音乐播放方法,代码如下:
# 音乐播放器self.player = MusicPlayer('game_music.ogg')self.player.play_music()
(3)在Game类的event_handler方法中切换背景音乐状态
else:# 游戏还没结束,切换暂停状态self.is_game_pause = not self.is_game_pauseself.player.pause_music(self.is_game_pause) # 切换背景音乐状态
3.7.1 加载和播放音效
(1)发射子弹
在event_handler方法中找到监听发射子弹事件的分支,代码如下:
elif event.type == HERO_FIRE_EVENT:# 英雄飞机发射子弹定时事件self.player.play_sound('bullet.wav')self.hero_sprite.fire(self.all_group)
(2)引爆炸弹
在event_handler方法中找到监听玩家按下字母B引爆炸弹的分支,代码如下:
# 必须在游戏未结束也没暂停才能执行的操作if not self.is_game_over and not self.is_game_pause:if event.type == pygame.KEYDOWN and event.key == pygame.K_b:# 释放一颗炸弹,并修改炸弹数量if self.hero_sprite.hp > 0 and self.hero_sprite.bomb_count > 0:self.player.play_sound('use_bomb.wav')
(3)投放和拾取道具
在event_handler方法中找到监听投放道具事件的分支,代码如下:
elif event.type == THROW_SUPPLY_EVENT:# 随机投放一个道具self.player.play_sound('supply.wav')supply = random.choice(self.supplies_group.sprites())supply.throw_supply()
(4)敌机爆炸
在check_collide方法中,找到检测敌机被子弹击中部分的代码,调整代码如下:
# 当前这一颗子弹已经把敌机摧毁if self.hud_panel.increase_score(enemy.value):self.player.play_sound('upgrade.wav')self.create_enemies()
(5)英雄飞机爆炸
在check_collide方法中,找到检测英雄飞机和敌机碰撞部分的代码,调整代码如下:
# 撞毁玩家飞机if collide_enemies:self.player.play_sound(self.hero_sprite.wav_name)self.hero_sprite.hp = 0
4.实验结果
5.总结和展望
基于pygame实现的飞机大战游戏相关推荐
- 【java毕业设计】基于java+Eclipse的飞机大战游戏设计与实现(毕业论文+程序源码)——飞机大战游戏
基于java+Eclipse的飞机大战游戏设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于java+Eclipse的飞机大战游戏设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦. ...
- 基于IntelliJ IDEA的飞机大战游戏设计与实现
目 录 1 引言 1 1.1 项目背景 1 2 国内外研究现状 3 1.3 项目主要工作 4 1.4 本文组织结构 6 2 开发平台与开发技术 7 1 IntelliJ IDEA简介 7 2. 2 I ...
- 【python】使用pygame写的飞机大战游戏
制作原因 做这个小游戏是因为学校的实训内容,老师给的要求是飞机大战,当时我感觉飞机大战这个题材太多了,就做了个飞艇大战,图片也是很久之前学习前端时攒下的素材,自己用ps改了改拿来用效果还不错 因为实训 ...
- 无聊消遣——基于pygame库的飞机大战
前一段在数据分析中突然感觉到了一阵阵的空虚寂寞冷,所以我决定小小放松一下,于是萌生出了写一个小游戏的想法.所以在pygame中摸索了2天,终于完成了无聊的飞机大战代码.之前从来没写过游戏,所以感觉还蛮 ...
- c语言基于easyx库的 飞机大战游戏(鼠标控制飞机移动,武器升级,boss发射散弹,boss血条等功能)
课设题目 实现功能: 飞机鼠标控制–飞机武器升级–敌机发射子弹–boss发射散弹–boss血条记录–我方多条生命 图片资源和源码在下面 链接:https://pan.baidu.com/s/1uTQV ...
- [内附完整源码和文档] 基于C++的空中飞机大战游戏
一.问题描述 项目选题为做一个飞行射击游戏.游戏中有各总各样的敌机和武器,敌机按一定的几率出现,玩家消灭敌机后可得分数奖励,达到一定分数后会升级,游戏难度随着玩家级数的增加而增大.游戏有很多爆炸场面, ...
- Python实验,用pygame做飞机大战游戏设计
飞机大战游戏设计 摘 要:根据课程要求,以及面向对象程序设计的编程思想,在Windows操作系统环境下,运用PyCharm编译程序,以Python语言为开发语言,最终实现飞机大战游戏相应的游戏操作功能 ...
- 飞机大战游戏---Pygame
一.基础知识 pygame安装 pygame就是一个Python模块,专为店子游戏设计 安装pygame(Windows): pip install pygame 验证安装 python -m pyg ...
- 飞机大战游戏python_基于Python的飞机大战游戏
基于 Python 的飞机大战游戏 杨铠 [期刊名称] <电脑迷> [年 ( 卷 ), 期] 2017(000)021 [摘要] 我们每天都享受到科技带来的好处 , 了解计算机编程对每个人 ...
最新文章
- 零基础入门学习Python(26)-文件1
- vector 用法小例子
- 移动端前端常见的触摸相关事件touch、tap、swipe等整理
- 大剑无锋之Hive调优【面试推荐】
- python下载大文件
- 由于在客户端检测到一个协议错误_HTTP协议,你了解多少?
- idea粘贴代码为什么都在一行_【学园】今天程序员的每一行代码都是未来高达身上的一颗螺丝...
- 使用CSVDE批量创建和修改域用户
- CMOS模拟集成电路笔记(第二部分)
- Ethernet和802.3的区别
- 【Java基础】Lambda表达式的使用
- QEMUKVM 虚拟机使用实例
- HashMap的七种遍历方式
- 认知学习:当代学习理论的主流
- plc实验报告流程图_plc实验报告
- 评郭德纲《论50年相声之现状》
- java对焦_Android camera摄像头对焦,zoom的通知事件回调,告诉java应用层已经对焦完成...
- 什么样的企业算是负责任的企业
- I2C 专题(一)I2C 简介
- 【爬虫】爬取B站UP的所有视频细节信息(通过UP名字)
热门文章
- C++对C的加强之C++中所有的变量和函数都必须有类型
- C语言合并排序实例代码
- Cloudera Manager 和CDH6.0.1安装,卸载,各步骤截图(此博文为笔者辛苦劳作最终生成的,使用了3个熬到凌晨2~4点的夜晚,外加一个周末完成,请转载时记录转载之处,谢谢)
- 2.cocos2d-x坐标体系(UI坐标系,GL坐标系,本地坐标,世界坐标,节点坐标)
- 窗口分析函数_16_找出最后一个元素
- keras用cpu加速_在训练某些网络时,Keras(Tensorflow后端)在GPU上比在CPU上慢CPU
- 信号量、互斥体和自旋锁
- Quartus ii与Modelsim-altera 6.5b联调前仿真
- springboot配置Filter的两种方法
- LVS DR模式搭建,keepalived + LVS