Python项目 您所在的位置:网站首页 飞机大战游戏项目描述 Python项目

Python项目

2023-12-17 03:38| 来源: 网络整理| 查看: 265

文章目录 一、项目介绍——飞机大战实战步骤确认模块 —— pygame安装 pygame验证安装 二、pygame 快速入门项目准备1. 使用 `pygame` 创建图形窗口小节目标1.1 游戏的初始化和退出1.2 理解游戏中的坐标系案例演练 1.3 创建游戏主窗口1.4 简单的游戏循环 2. 理解 **图像** 并实现图像绘制代码演练 I —— 绘制背景图像代码演练 II —— 绘制英雄图像理解 `update()` 方法的作用 3. 理解 **游戏循环** 和 **游戏时钟**3.1 游戏中的动画实现原理3.2 **游戏循环**游戏的两个组成部分游戏循环的作用 3.3 游戏时钟3.4 英雄的简单动画实现作业 3.5 在游戏循环中 监听 事件事件 `event`监听代码实现 4. 理解 **精灵** 和 **精灵组**4.1 精灵 和 精灵组精灵精灵组 4.2 派生精灵子类4.3 使用 游戏精灵 和 精灵组 创建敌机实现步骤 三、游戏框架搭建1. 明确主程序职责2. 实现飞机大战主游戏类2.1 明确文件职责代码实现 2.3 游戏初始化部分使用 常量 代替固定的数值 2.4 游戏循环部分 3. 准备游戏精灵组3.1 确定精灵组3.2 代码实现 四、游戏背景滚动1. 背景交替滚动的思路确定1.1 实现思路分析1.2 设计背景类 2. 显示游戏背景2.1 背景精灵的基本实现2.2 在 `plane_main.py` 中显示背景精灵2.3 利用初始化方法,简化背景精灵创建 五、敌机出场1. 使用定时器添加敌机1.1 定时器1.2 定义并监听创建敌机的定时器事件1) 定义事件2) 监听定时器事件 2. 设计 `Enemy` 类2.1 敌机类的准备2.2 创建敌机2.3 随机敌机位置和速度1) 导入模块2) 随机位置3) 代码实现 2.4 移出屏幕销毁敌机检测敌机被销毁代码实现 六、英雄登场1. 设计 **英雄** 和 **子弹** 类英雄需求子弹需求Hero —— 英雄Bullet —— 子弹 2. 创建英雄2.1 准备英雄类2.2 绘制英雄代码实现 3. 移动英雄位置3.1 移动英雄位置3.2 控制英雄运动边界 4. 发射子弹需求回顾 —— 英雄需求4.1 添加发射子弹事件4.2 定义子弹类需求回顾 —— 子弹需求Bullet —— 子弹定义子弹类 4.3 发射子弹一次发射三枚子弹 七、碰撞检测1. 了解碰撞检测方法pygame.sprite.groupcollide()pygame.sprite.spritecollide() 2. 碰撞实现

一、项目介绍——飞机大战

按照黑马的教程完成飞机大战项目,我们的项目最终目标如下:

代码放在Gitee上了:https://gitee.com/aniu251700/python-aircraft-war

在这里插入图片描述

在这里插入图片描述

实战步骤 pygame 快速体验飞机大战 实战 确认模块 —— pygame pygame 就是一个 Python 模块,专为电子游戏设计官方网站:https://www.pygame.org/ 提示:要学习第三方模块,通常最好的参考资料就在官方网站 网站栏目内容GettingStarted在各平台安装模块的说明Docspygame 模块所有 类 和 子类 的参考手册 安装 pygame $ sudo pip3 install pygame 验证安装 $ python3 -m pygame.examples.aliens

输入后会出现这样一个游戏,就表示安装成功了

在这里插入图片描述

二、pygame 快速入门 项目准备使用 pygame 创建图形窗口理解 图像 并实现图像绘制理解 游戏循环 和 游戏时钟理解 精灵 和 精灵组 项目准备 新建 飞机大战 项目新建一个 hm_01_pygame入门.py导入 游戏素材图片

游戏的第一印象

把一些 静止的图像 绘制到 游戏窗口 中根据 用户的交互 或其他情况,移动 这些图像,产生动画效果根据 图像之间 是否发生重叠,判断 敌机是否被摧毁 等其他情况 1. 使用 pygame 创建图形窗口 小节目标 游戏的初始化和退出理解游戏中的坐标系创建游戏主窗口简单的游戏循环

可以将图片素材 绘制 到 游戏的窗口 上,开发游戏之前需要先知道 如何建立游戏窗口!

1.1 游戏的初始化和退出 要使用 pygame 提供的所有功能之前,需要调用 init 方法在游戏结束前需要调用一下 quit 方法 方法说明pygame.init()导入并初始化所有 pygame 模块,使用其他模块之前,必须先调用 init 方法pygame.quit()卸载所有 pygame 模块,在游戏结束之前调用!

在这里插入图片描述

import pygame pygame.init() # 游戏代码... pygame.quit() 1.2 理解游戏中的坐标系 坐标系 原点 在 左上角 (0, 0)x 轴 水平方向向 右,逐渐增加y 轴 垂直方向向 下,逐渐增加

在这里插入图片描述

在游戏中,所有可见的元素 都是以 矩形区域 来描述位置的

要描述一个矩形区域有四个要素:(x, y) (width, height)

pygame 专门提供了一个类 pygame.Rect 用于描述 矩形区域

Rect(x, y, width, height) -> Rect

在这里插入图片描述

提示

pygame.Rect 是一个比较特殊的类,内部只是封装了一些数字计算不执行 pygame.init() 方法同样能够直接使用 案例演练

需求

定义 hero_rect 矩形描述 英雄的位置和大小输出英雄的 坐标原点(x 和 y)输出英雄的 尺寸(宽度 和 高度) hero_rect = pygame.Rect(100, 500, 120, 126) print("坐标原点 %d %d" % (hero_rect.x, hero_rect.y)) # 100 500 print("英雄大小 %d %d" % (hero_rect.width, hero_rect.height)) # 120 126 # size 属性会返回矩形区域的 (宽, 高) 元组 print("英雄大小 %d %d" % hero_rect.size) # 120 126 1.3 创建游戏主窗口 pygame 专门提供了一个 模块 pygame.display 用于创建、管理 游戏窗口 方法说明pygame.display.set_mode()初始化游戏显示窗口pygame.display.update()刷新屏幕内容显示,稍后使用

set_mode 方法

set_mode(resolution=(0,0), flags=0, depth=0) -> Surface

作用 —— 创建游戏显示窗口

参数

resolution 指定屏幕的 宽 和 高,默认创建的窗口大小和屏幕大小一致flags 参数指定屏幕的附加选项,例如是否全屏等等,默认不需要传递depth 参数表示颜色的位数,默认自动匹配

返回值

暂时 可以理解为 游戏的屏幕,游戏的元素 都需要被绘制到 游戏的屏幕 上

注意:必须使用变量记录 set_mode 方法的返回结果!因为:后续所有的图像绘制都基于这个返回结果

# 创建游戏主窗口 screen = pygame.display.set_mode((480, 700)) 1.4 简单的游戏循环 为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中增加一个 游戏循环所谓 游戏循环 就是一个 无限循环在 创建游戏窗口 代码下方,增加一个无限循环 注意:游戏窗口不需要重复创建 # 创建游戏主窗口 screen = pygame.display.set_mode((480, 700)) # 游戏循环 while True: pass 2. 理解 图像 并实现图像绘制 在游戏中,能够看到的 游戏元素 大多都是 图像 图像文件 初始是保存在磁盘上的,如果需要使用,第一步 就需要 被加载到内存 要在屏幕上 看到某一个图像的内容,需要按照三个步骤: 使用 pygame.image.load() 加载图像的数据使用 游戏屏幕 对象,调用 blit 方法 将图像绘制到指定位置调用 pygame.display.update() 方法更新整个屏幕的显示

在这里插入图片描述

提示:要想在屏幕上看到绘制的结果,就一定要调用 pygame.display.update() 方法

代码演练 I —— 绘制背景图像

需求

加载 background.png 创建背景将 背景 绘制在屏幕的 (0, 0) 位置调用屏幕更新显示背景图像 # 绘制背景图像 # 1> 加载图像 bg = pygame.image.load("./images/background.png") # 2> 绘制在屏幕 screen.blit(bg, (0, 0)) # 3> 更新显示 pygame.display.update() 代码演练 II —— 绘制英雄图像

需求

加载 me1.png 创建英雄飞机将 英雄飞机 绘制在屏幕的 (200, 500) 位置调用屏幕更新显示飞机图像 # 1> 加载图像 hero = pygame.image.load("./images/me1.png") # 2> 绘制在屏幕 screen.blit(hero, (200, 500)) # 3> 更新显示 pygame.display.update()

透明图像

png 格式的图像是支持 透明 的在绘制图像时,透明区域 不会显示任何内容但是如果下方已经有内容,会 透过 透明区域 显示出来

在这里插入图片描述

理解 update() 方法的作用

可以在 screen 对象完成 所有 blit 方法之后,统一调用一次 display.update 方法,同样可以在屏幕上 看到最终的绘制结果

使用 display.set_mode() 创建的 screen 对象 是一个 内存中的屏幕数据对象 可以理解成是 油画 的 画布 screen.blit 方法可以在 画布 上绘制很多 图像 例如:英雄、敌机、子弹…这些图像 有可能 会彼此 重叠或者覆盖 display.update() 会将 画布 的 最终结果 绘制在屏幕上,这样可以 提高屏幕绘制效率,增加游戏的流畅度

案例调整

# 绘制背景图像 # 1> 加载图像 bg = pygame.image.load("./images/background.png") # 2> 绘制在屏幕 screen.blit(bg, (0, 0)) # 绘制英雄图像 # 1> 加载图像 hero = pygame.image.load("./images/me1.png") # 2> 绘制在屏幕 screen.blit(hero, (200, 500)) # 3> 更新显示 - update 方法会把之前所有绘制的结果,一次性更新到屏幕窗口上 pygame.display.update() 3. 理解 游戏循环 和 游戏时钟

现在 英雄飞机 已经被绘制到屏幕上了,怎么能够让飞机移动呢 ?

3.1 游戏中的动画实现原理 跟 电影 的原理类似,游戏中的动画效果,本质上是 快速 的在屏幕上绘制 图像 电影是将多张 静止的电影胶片 连续、快速的播放,产生连贯的视觉效果! 一般在电脑上 每秒绘制 60 次,就能够达到非常 连续 高品质 的动画效果 每次绘制的结果被称为 帧 Frame

在这里插入图片描述

在这里插入图片描述

3.2 游戏循环 游戏的两个组成部分

游戏循环的开始 就意味着 游戏的正式开始

在这里插入图片描述

游戏循环的作用 保证游戏 不会直接退出变化图像位置 —— 动画效果 每隔 1 / 60 秒 移动一下所有图像的位置调用 pygame.display.update() 更新屏幕显示 检测用户交互 —— 按键、鼠标等… 3.3 游戏时钟 pygame 专门提供了一个类 pygame.time.Clock 可以非常方便的设置屏幕绘制速度 —— 刷新帧率要使用 时钟对象 需要两步: 1)在 游戏初始化 创建一个 时钟对象2)在 游戏循环 中让时钟对象调用 tick(帧率) 方法 tick 方法会根据 上次被调用的时间,自动设置 游戏循环 中的延时 # 3. 创建游戏时钟对象 clock = pygame.time.Clock() i = 0 # 游戏循环 while True: # 设置屏幕刷新帧率 clock.tick(60) print(i) i += 1 3.4 英雄的简单动画实现

需求

在 游戏初始化 定义一个 pygame.Rect 的变量记录英雄的初始位置在 游戏循环 中每次让 英雄 的 y - 1 —— 向上移动y None set_timer 可以创建一个 事件可以在 游戏循环 的 事件监听 方法中捕获到该事件第 1 个参数 事件代号 需要基于常量 pygame.USEREVENT 来指定 USEREVENT 是一个整数,再增加的事件可以使用 USEREVENT + 1 指定,依次类推… 第 2 个参数是 事件触发 间隔的 毫秒值

定时器事件的监听

通过 pygame.event.get() 可以获取当前时刻所有的 事件列表遍历列表 并且判断 event.type 是否等于 eventid,如果相等,表示 定时器事件 发生 1.2 定义并监听创建敌机的定时器事件

pygame 的 定时器 使用套路非常固定:

定义 定时器常量 —— eventid在 初始化方法 中,调用 set_timer 方法 设置定时器事件在 游戏循环 中,监听定时器事件 1) 定义事件 在 plane_sprites.py 的顶部定义 事件常量 # 敌机的定时器事件常量 CREATE_ENEMY_EVENT = pygame.USEREVENT 在 PlaneGame 的 初始化方法 中 创建用户事件 # 4. 设置定时器事件 - 每秒创建一架敌机 pygame.time.set_timer(CREATE_ENEMY_EVENT, 1000) 2) 监听定时器事件 在 __event_handler 方法中增加以下代码: def __event_handler(self): for event in pygame.event.get(): # 判断是否退出游戏 if event.type == pygame.QUIT: PlaneGame.__game_over() elif event.type == CREATE_ENEMY_EVENT: print("敌机出场...") 2. 设计 Enemy 类 游戏启动后,每隔 1 秒 会 出现一架敌机每架敌机 向屏幕下方飞行,飞行 速度各不相同每架敌机出现的 水平位置 也不尽相同当敌机 从屏幕下方飞出,不会再飞回到屏幕中

在这里插入图片描述

初始化方法 指定 敌机图片随机 敌机的 初始位置 和 初始速度 重写 update() 方法 判断 是否飞出屏幕,如果是,从 精灵组 删除 2.1 敌机类的准备 在 plane_sprites 新建 Enemy 继承自 GameSprite重写 初始化方法,直接指定 图片名称暂时 不实现 随机速度 和 随机位置 的指定重写 update 方法,判断是否飞出屏幕 class Enemy(GameSprite): """敌机精灵""" def __init__(self): # 1. 调用父类方法,创建敌机精灵,并且指定敌机的图像 super().__init__("./images/enemy1.png") # 2. 设置敌机的随机初始速度 # 3. 设置敌机的随机初始位置 def update(self): # 1. 调用父类方法,让敌机在垂直方向运动 super().update() # 2. 判断是否飞出屏幕,如果是,需要将敌机从精灵组删除 if self.rect.y >= SCREEN_RECT.height: print("敌机飞出屏幕...") 2.2 创建敌机

演练步骤

在 __create_sprites,添加 敌机精灵组 敌机是 定时被创建的,因此在初始化方法中,不需要创建敌机 在 __event_handler,创建敌机,并且 添加到精灵组 调用 精灵组 的 add 方法可以 向精灵组添加精灵 在 __update_sprites,让 敌机精灵组 调用 update 和 draw 方法

在这里插入图片描述

演练代码

修改 plane_main 的 __create_sprites 方法 # 敌机组 self.enemy_group = pygame.sprite.Group() 修改 plane_main 的 __update_sprites 方法 self.enemy_group.update() self.enemy_group.draw(self.screen) 定时出现敌机 elif event.type == CREATE_ENEMY_EVENT: self.enemy_group.add(Enemy()) 2.3 随机敌机位置和速度 1) 导入模块 在导入模块时,建议 按照以下顺序导入 1. 官方标准模块导入 2. 第三方模块导入 3. 应用程序模块导入 修改 plane_sprites.py 增加 random 的导入 import random 2) 随机位置

在这里插入图片描述

使用 pygame.Rect 提供的 bottom 属性,在指定敌机初始位置时,会比较方便

bottom = y + heighty = bottom - height 3) 代码实现 修改 初始化方法,随机敌机出现 速度 和 位置 def __init__(self): # 1. 调用父类方法,创建敌机精灵,并且指定敌机的图像 super().__init__("./images/enemy1.png") # 2. 设置敌机的随机初始速度 1 ~ 3 self.speed = random.randint(1, 3) # 3. 设置敌机的随机初始位置 self.rect.bottom = 0 max_x = SCREEN_RECT.width - self.rect.width self.rect.x = random.randint(0, max_x) 2.4 移出屏幕销毁敌机 敌机移出屏幕之后,如果 没有撞到英雄,敌机的历史使命已经终结需要从 敌机组 删除,否则会造成 内存浪费 检测敌机被销毁 __del__ 内置方法会在对象被销毁前调用,在开发中,可以用于 判断对象是否被销毁 def __del__(self): print("敌机挂了 %s" % self.rect) 代码实现

在这里插入图片描述

判断敌机是否飞出屏幕,如果是,调用 kill() 方法从所有组中删除 def update(self): super().update() # 判断敌机是否移出屏幕 if self.rect.y >= SCREEN_RECT.height: # 将精灵从所有组中删除 self.kill() 六、英雄登场 设计 英雄 和 子弹 类使用 pygame.key.get_pressed() 移动英雄发射子弹 1. 设计 英雄 和 子弹 类 英雄需求 游戏启动后,英雄 出现在屏幕的 水平中间 位置,距离 屏幕底部 120 像素英雄 每隔 0.5 秒发射一次子弹,每次 连发三枚子弹英雄 默认不会移动,需要通过 左/右 方向键,控制 英雄 在水平方向移动

在这里插入图片描述

子弹需求 子弹 从 英雄 的正上方发射 沿直线 向 上方 飞行飞出屏幕后,需要从 精灵组 中删除

在这里插入图片描述

Hero —— 英雄 初始化方法 指定 英雄图片初始速度 = 0 —— 英雄默认静止不动定义 bullets 子弹精灵组 保存子弹精灵 重写 update() 方法 英雄需要 水平移动并且需要保证不能 移出屏幕 增加 bullets 属性,记录所有 子弹精灵增加 fire 方法,用于发射子弹 Bullet —— 子弹 初始化方法 指定 子弹图片初始速度 = -2 —— 子弹需要向上方飞行 重写 update() 方法 判断 是否飞出屏幕,如果是,从 精灵组 删除 2. 创建英雄 2.1 准备英雄类 在 plane_sprites 新建 Hero 类重写 初始化方法,直接指定 图片名称,并且将初始速度设置为 0设置 英雄的初始位置

在这里插入图片描述

centerx = x + 0.5 * widthcentery = y + 0.5 * heightbottom = y + height

在这里插入图片描述

class Hero(GameSprite): """英雄精灵""" def __init__(self): super().__init__("./images/me1.png", 0) # 设置初始位置 self.rect.centerx = SCREEN_RECT.centerx self.rect.bottom = SCREEN_RECT.bottom - 120 2.2 绘制英雄 在 __create_sprites,添加 英雄精灵 和 英雄精灵组 后续要针对 英雄 做 碰撞检测 以及 发射子弹所以 英雄 需要 单独定义成属性 在 __update_sprites,让 英雄精灵组 调用 update 和 draw 方法 代码实现 修改 __create_sprites 方法如下: # 英雄组 self.hero = Hero() self.hero_group = pygame.sprite.Group(self.hero) 修改 __update_sprites 方法如下: self.hero_group.update() self.hero_group.draw(self.screen) 3. 移动英雄位置

在 pygame 中针对 键盘按键的捕获,有 两种 方式

第一种方式 判断 event.type == pygame.KEYDOWN第二种方式 首先使用 pygame.key.get_pressed() 返回 所有按键元组通过 键盘常量,判断元组中 某一个键是否被按下 —— 如果被按下,对应数值为 1

提问 这两种方式之间有什么区别呢?

第一种方式 elif event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT: print("向右移动...") 第二种方式 # 返回所有按键的元组,如果某个键被按下,对应的值会是1 keys_pressed = pygame.key.get_pressed() # 判断是否按下了方向键 if keys_pressed[pygame.K_RIGHT]: print("向右移动...")

结论

第一种方式 event.type 用户 必须要抬起按键 才算一次 按键事件,操作灵活性会大打折扣第二种方式 用户可以按住方向键不放,就能够实现持续向某一个方向移动了,操作灵活性更好 3.1 移动英雄位置

演练步骤

在 Hero 类中重写 update 方法 用 速度 speed 和 英雄 rect.x 进行叠加不需要调用父类方法 —— 父类方法只是实现了单纯的垂直运动 在 __event_handler 方法中根据 左右方向键 设置英雄的 速度 向右 => speed = 2向左 => speed = -2其他 => speed = 0

代码演练

在 Hero 类,重写 update() 方法,根据速度水平移动 英雄的飞机 def update(self): # 飞机水平移动 self.rect.x += self.speed 调整键盘按键代码 # 获取用户按键 keys_pressed = pygame.key.get_pressed() if keys_pressed[pygame.K_RIGHT]: self.hero.speed = 2 elif keys_pressed[pygame.K_LEFT]: self.hero.speed = -2 else: self.hero.speed = 0 3.2 控制英雄运动边界 在 Hero 类的 update() 方法判断 英雄 是否超出 屏幕边界

在这里插入图片描述

right = x + width 利用 right 属性可以非常容易的针对右侧设置精灵位置

在这里插入图片描述

def update(self): # 飞机水平移动 self.rect.x += self.speed # 判断屏幕边界 if self.rect.left SCREEN_RECT.right: self.rect.right = SCREEN_RECT.right 4. 发射子弹 需求回顾 —— 英雄需求 游戏启动后,英雄 出现在屏幕的 水平中间 位置,距离 屏幕底部 120 像素英雄 每隔 0.5 秒发射一次子弹,每次 连发三枚子弹英雄 默认不会移动,需要通过 左/右 方向键,控制 英雄 在水平方向移动 4.1 添加发射子弹事件

pygame 的 定时器 使用套路非常固定:

定义 定时器常量 —— eventid在 初始化方法 中,调用 set_timer 方法 设置定时器事件在 游戏循环 中,监听定时器事件

代码实现

在 Hero 中定义 fire 方法 def fire(self): print("发射子弹...") 在 plane_main.py 的顶部定义 发射子弹 事件常量 # 英雄发射子弹事件 HERO_FIRE_EVENT = pygame.USEREVENT + 1 在 __init__ 方法末尾中添加 发射子弹 事件 # 每隔 0.5 秒发射一次子弹 pygame.time.set_timer(HERO_FIRE_EVENT, 500) 在 __event_handler 方法中让英雄发射子弹 elif event.type == HERO_FIRE_EVENT: self.hero.fire() 4.2 定义子弹类 需求回顾 —— 子弹需求 子弹 从 英雄 的正上方发射 沿直线 向 上方 飞行飞出屏幕后,需要从 精灵组 中删除 Bullet —— 子弹 初始化方法 指定 子弹图片初始速度 = -2 —— 子弹需要向上方飞行 重写 update() 方法 判断 是否飞出屏幕,如果是,从 精灵组 删除 定义子弹类 在 plane_sprites 新建 Bullet 继承自 GameSprite重写 初始化方法,直接指定 图片名称,并且设置 初始速度重写 update() 方法,判断子弹 飞出屏幕从精灵组删除 class Bullet(GameSprite): """子弹精灵""" def __init__(self): super().__init__("./images/bullet1.png", -2) def update(self): super().update() # 判断是否超出屏幕,如果是,从精灵组删除 if self.rect.bottom Sprite_dict 如果将 dokill 设置为 True,则 发生碰撞的精灵将被自动移除collided 参数是用于 计算碰撞的回调函数 如果没有指定,则每个精灵必须有一个 rect 属性 pygame.sprite.spritecollide() 判断 某个精灵 和 指定精灵组 中的精灵的碰撞 spritecollide(sprite, group, dokill, collided = None) -> Sprite_list 如果将 dokill 设置为 True,则 指定精灵组 中 发生碰撞的精灵将被自动移除collided 参数是用于 计算碰撞的回调函数 如果没有指定,则每个精灵必须有一个 rect 属性 返回 精灵组 中跟 精灵 发生碰撞的 精灵列表 2. 碰撞实现 def __check_collide(self): # 1. 子弹摧毁敌机 pygame.sprite.groupcollide(self.hero.bullets, self.enemy_group, True, True) # 2. 敌机撞毁英雄 enemies = pygame.sprite.spritecollide(self.hero, self.enemy_group, True) # 判断列表时候有内容 if len(enemies) > 0: # 让英雄牺牲 self.hero.kill() # 结束游戏 PlaneGame.__game_over()

此时我们整个游戏就完成了!!!



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有