目标
1,项目准备
2,使用pygame
创建图形窗口 3,理解 图像 并实现图像绘制 4,理解 游戏循环 和 游戏时钟 5,理解 精灵 和精灵组** 项目准备
1,新建 飞机大战 项目
2,新建一个fly_01_pygame.py
3,导入 游戏素材图片 游戏的第一印象
- 把一些 静止的图像 会知道 游戏窗口 中
- 根据 用户的交互 或其他情况,移动 这些图像,产生动画效果
- 根据 图像之间 是否发生重叠,判断 敌机是否被摧毁,等其他情况
01,使用 pygame 创建图形窗口
小节目标
1,游戏的初始化和退出
2,理解游戏中的坐标系 3,创建游戏主窗口 4,简单的游戏循环注 可以将图片素材 绘制 到 游戏的窗口 上,开发游戏之前需要先知道 如何建立游戏窗口
1.1 游戏的初始化和退出
- 要使用
pygame
提供的所有功能之前,需要调用init
方法 - 在游戏结束前需要调用一下
quit
方法
方法 | 说明 |
---|---|
pygame.init() | 导入并初始化所有 pygame模块,使用其他模块之前,必须先调用 init 方法 |
pygame.quit() | 卸载pygame模块,在游戏结束之前调用 |
import pygamepygame.init()# 编写游戏代码print("游戏代码。。。")pygame.quit()### 结果呈现pygame 1.9.4Hello from the pygame community. https://www.pygame.org/contribute.html游戏代码。。。
1.2 理解游戏中的坐标系
- 坐标系
- 原点 在 左上角
(0, 0)
- x轴 水平方向向 右,逐渐增加
- y轴 水平方向向 下,逐渐增加
- 原点 在 左上角
- 在游戏中,所有可见的元素 都是以 矩形区域 来描述位置的
- 要描述一个矩形区域有四个要素:
(x, y) (width, height)
- 要描述一个矩形区域有四个要素:
pygame
专门提供一个类pygame.Rect
用于描述 矩形区域
Rect(x, y, width, height) -> Rect
提示
pygame.Rect
是一个比较特殊的类,内部只是封装了一些数字计算- 不执行
pygame.init()
方法同样可以直接使用
案例演练
需求
1,定义 hero_rect
矩形描述 英雄的位置和大小
(x, y)
3,输出英雄的 尺寸 (宽度 和 高度) import pygame# 定义 hero_rect 矩形描述 英雄的位置和大小hero_rect = pygame.Rect(100, 500, 120, 125)print("英雄的原点 %d %d" % (hero_rect.x, hero_rect.y))print("英雄的尺寸 %d %d" % (hero_rect.width, hero_rect.height))print("%d %d" % hero_rect.size)# 结果呈现pygame 1.9.4Hello from the pygame community. https://www.pygame.org/contribute.html英雄的原点 100 500英雄的尺寸 120 125120 125
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))
import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))pygame.quit()
1.4 简单的邮箱循环
- 为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中增加一个 无限循环
- 所谓 游戏循环 就是一个 无限循环
- 在 创建游戏窗口 代码下方,增加一个无限循环
- 注意:**游戏窗口不需要重复创建
import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 游戏循环while True: passpygame.quit()
02,理解 图像 并实现图像绘制
- 在游戏中,能够看到 游戏元素 大多都是 图像
- 图像文件 初始是保存在磁盘上的,如果需要使用,第一步 就需要 被加载到内存
- 要在屏幕上 看到某一个图像的内容,需要按照三个步骤:
- 使用
pygame.image.load()
加载图像的数据 - 使用 游戏屏幕 对象,调用
blit
方法 将图像绘制到指定位置 - 调用
pygame.display.update()
方法更新整个显示
- 使用
提示:要想在屏幕上看到绘制的结果,就一定要调用 pygame.display.update()
方法
代码演练I - - 绘制背景图像
1,加载 background.png
创建背景
(0, 0)
位置 3,调用屏幕更新显示背景图像 import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 1,加载 图像数据bg = pygame.image.load("./images/background.png")# 2,blit 绘制图像screen.blit(bg, (0, 0))# 3,update 更新屏幕显示pygame.display.update()while True: passpygame.quit()
代码演练 II - - 绘制英雄图像
需求
1,加载me1
创建英雄飞机 2,将 英雄飞机 绘制在屏幕的 (200, 500)
位置 3,调用屏幕更新显示飞机图像 import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 1,加载 图像数据bg = pygame.image.load("./images/background.png")# 2,blit 绘制图像screen.blit(bg, (0, 0))# 绘制英雄的飞机hero = pygame.image.load("./images/me1.png")screen.blit(hero, (200, 500))# 3,update 更新屏幕显示pygame.display.update()while True: passpygame.quit()
透明图像
png
格式的图像是支持 透明 的- 在绘制图像时,透明区域 不会显示任何内容
- 但是如果 下方已经有内容,会 透过 透明区域** 显示出来
理解 update() 方法的作用
可以在 screen
对象完成 所有 blit
方法之后,统一调用一次 display.update()
方法,同样可以在屏幕上 看到最终绘制的结果
- 使用
display.set_mode()
创建的screen
对象 是一个 内存中的屏幕数据对象- 可以理解成 油画 的 画布
screen.blit
方法可以在 画布 上绘制很多 图像- 例如:英雄、敌机、子弹
- 这些图像 有可能 会彼此 重叠或者覆盖
display.update()
会将 画布 的 最终结果 绘制在屏幕上,这样可以 提高屏幕绘制效率,增加游戏的流畅度
03,理解 游戏循环 和 游戏时钟
现在 英雄飞机 已经被绘制到屏幕上了,怎么能够让飞机移动
3.1 游戏中的动画实现原理
- 跟 电影 的原理类似,游戏中的动画效果,本质上就是 快速 在屏幕上绘制 图像
- 定义是将多张 静止的电源胶片 连续、快速 的播放
- 一般在电脑上 每秒绘制60次,就能达到非常 连续 高品质 的动画效果
- 每次绘制的结果称为 帧 Frame
3.2 游戏循环
游戏的两个组成部分
注 游戏循环的开始 就是 意味着 游戏的正式开始
游戏循环的作用
1,保证游戏 不会直接退出
2,变化图像位置 - - 动画效果- 每隔
1 / 60 秒
移动一下所有图像的位置 - 调用
pygame.display.update()
根新屏幕显示 3,检测用户交互 - - 按键,鼠标等。。。
3.3 游戏时钟
pygame
专门提供了一个类pygame.time.Clock
可以非常方便的设置屏幕绘制速度 - - 刷新频率- 要使用 时钟对象 需要两步:
- 在 游戏初始化 创建一个 时钟对象
- 在 游戏循环 中让时钟对象调用
tick(帧率)
方法
tick
方法会根据 上次被调用的时间,自动设置 游戏循环 中的延时
import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 1,加载 图像数据bg = pygame.image.load("./images/background.png")# 2,blit 绘制图像screen.blit(bg, (0, 0))# 绘制英雄的飞机hero = pygame.image.load("./images/me1.png")screen.blit(hero, (200, 500))# 3,update 更新屏幕显示pygame.display.update()# 创建时钟对象clock = pygame.time.Clock()# 游戏循环 -> 意味着游戏的正式开始i = 0while True: # 可以指定循环体内部的代码执行的频率 clock.tick(60) print(i) i += 1 passpygame.quit()
3.4英雄的简单动画实现
需求
1,在 游戏初始化 定义一个pygame.Rect
的变量记录英雄 2,在 游戏循环 中每次让 英雄 的 y - 1
- - 向上移动 3,y <= 0
将英雄移动到屏幕的底部 提示:
- 每一次调用
update()
方法之前,需要把 **所有的游戏图像都冲重新绘制以遍 - 而且应该 最先 重新绘制 背景图像
import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 加载 图像数据bg = pygame.image.load("./images/background.png")# blit 绘制图像screen.blit(bg, (0, 0))# 绘制英雄的飞机hero = pygame.image.load("./images/me1.png")screen.blit(hero, (200, 500))# update 更新屏幕显示pygame.display.update()# 创建时钟对象clock = pygame.time.Clock()# 1,定义rect 记录飞机的初始位置hero_rect = pygame.Rect(200, 500, 102, 126)# 游戏循环 -> 意味着游戏的正式开始while True: # 可以指定循环体内部的代码执行的频率 clock.tick(90) # 2,修改飞机的位置 hero_rect.y -= 1 # 判断飞机的位置 if hero_rect.bottom <= 0: hero_rect.y = 700 # 3,调用 blit 方法绘制图像 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) # 4,调用update方法更新显示 pygame.display.update() passpygame.quit()
提示
Rect
的属性bottom = y + height
if hero_rect.bottom <= 0: hero_rect.y = 700
3.5 在游戏循环中 监听 事件
事件 event
- 就是游戏启动后,用户针对游戏所做的操作
- 例如:点击关闭按钮、点击鼠标、点击键盘....
监听
- 在 游戏循环 中,判断用户 具体的操作注: 只有 捕获 到用户具体的操作,才能有针对性的做出响应
代码实现
pygame
中通过pygame.event.get()
可以获得 用户当前所有动作 的 事件列表- 用户可以同一时间做很多事情
- 提示:这段代码非常的固定,几乎所有的
pygame
游戏都 大同小异
事件监听
import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 加载 图像数据bg = pygame.image.load("./images/background.png")# blit 绘制图像screen.blit(bg, (0, 0))# 绘制英雄的飞机hero = pygame.image.load("./images/me1.png")screen.blit(hero, (200, 500))# update 更新屏幕显示pygame.display.update()# 创建时钟对象clock = pygame.time.Clock()# 定义rect 记录飞机的初始位置hero_rect = pygame.Rect(200, 500, 102, 126)# 游戏循环 -> 意味着游戏的正式开始while True: # 可以指定循环体内部的代码执行的频率 clock.tick(90) # 捕获事件 event_list = pygame.event.get() if len(event_list): print(event_list) # 修改飞机的位置 hero_rect.y -= 1 # 判断英雄的位置 if hero_rect.bottom <= 0: hero_rect.y = 700 # 调用 blit 方法绘制图像 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) # 调用update方法更新显示 pygame.display.update() passpygame.quit()# 结果呈现[][ ][ ][ ][ ][ ][ ][ ][ ][ ]
监听退出事件
import pygamepygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 加载 图像数据bg = pygame.image.load("./images/background.png")# blit 绘制图像screen.blit(bg, (0, 0))# 绘制英雄的飞机hero = pygame.image.load("./images/me1.png")screen.blit(hero, (200, 500))# update 更新屏幕显示pygame.display.update()# 创建时钟对象clock = pygame.time.Clock()# 定义rect 记录飞机的初始位置hero_rect = pygame.Rect(200, 500, 102, 126)# 游戏循环 -> 意味着游戏的正式开始while True: # 可以指定循环体内部的代码执行的频率 clock.tick(90) # 监听事件 for event in pygame.event.get(): # 判断事件类型是否是退出事件 if event.type == pygame.QUIT: print("游戏退出....") # quit 卸掉所有模块 pygame.quit() # exit() 直接终止正在运行的程序 exit() # 修改飞机的位置 hero_rect.y -= 1 # 判断英雄的位置 if hero_rect.bottom <= 0: hero_rect.y = 700 # 调用 blit 方法绘制图像 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) # 调用update方法更新显示 pygame.display.update() passpygame.quit()# 结果呈现游戏退出....
04,理解 精灵 和 精灵组
4.1 精灵 和 精灵组
- 在刚刚完成的案例中,图像加载、位置变化、绘制图像 都需要程序员编写代码
- 为了简化开发步骤,
pygame
提供了两个类pygame.sprite.Sprite
- - 存储 图像数据 image 和 位置 Rect 的对象pygame.sprite.Group
注意: 仍然需要调用 pygame.display.update()
才可以在屏幕看到最终结果
4.2 派生精灵子类
1,新建 plane_sprites.py
文件
GameSprite
继承自 pygame.sprite.Sprite
注意
- 如果一个类的 父类 不是
object
- 在重写 初始化方法 时,一定要 先
super()
一下父类的__init__
方法 - 保证父类中实现的
__init__
代码能够被正常执行
属性
image
精灵图像,使用image_name
加载rect
精灵大小,默认使用图像大小speed
精灵移动速度,默认为 1
方法
update
每次更新屏幕时在游戏循环内调用- 让精灵的
self.rect.y += self.speed
- 让精灵的
提示
image
的get_rect()
方法,可以返回 pygaem.Rect(0,0 图像宽, 图像高) 的对象
import pygameclass GameSprite(pygame.sprite.Sprite): """飞机大战游戏精灵""" def __init__(self, image_name, speed=1): # 调用父类的初始化方法 super().__init__() # 定义对象的属性 self.image = pygame.image.load(image_name) self.rect = self.image.get_rect() self.speed = speed def update(self, *args): # 在屏幕的垂直方向上移动 self.rect.y += self.speed
4.3 使用 游戏精灵 和 精灵组 创建敌机
需求
- 使用刚刚派生的 游戏精灵 和 精灵组 创建 敌机 并且实现敌机动画
步骤
1,使用from
导入 plane_sprites
模块 from
导入的模块可以 直接使用import
导入的模块需要通过 模块名. 来使用
2,在 游戏初始化 创建 精灵对象 和 精灵组对象
3,在 游戏循环中 让 精灵组 分别调用 update()
和 draw(screen)
方法
职责
- 精灵
- 封装 图像image、位置 rect 和速度 speed
- 提供
update()
方法,根据游戏需求,更新位置 rect
- 精灵组
- 包含 多个 精灵对象
update
方法,让精灵组中的所有精灵调用update
方法更新位置draw(screen)
方法,在screen
上绘制精灵组中的所有精灵
import pygamefrom Aircraft_War.plane_sprites import *# 游戏初始化pygame.init()# 创建游戏窗口screen = pygame.display.set_mode((480, 700))# 绘制背景图像# 加载 图像数据bg = pygame.image.load("./images/background.png")# blit 绘制图像screen.blit(bg, (0, 0))# 绘制英雄的飞机hero = pygame.image.load("./images/me1.png")screen.blit(hero, (200, 500))# update 更新屏幕显示pygame.display.update()# 创建时钟对象clock = pygame.time.Clock()# 定义rect 记录飞机的初始位置hero_rect = pygame.Rect(200, 500, 102, 126)# 创建敌机的精灵enemy = GameSprite("./images/enemy1.png")enemy1 = GameSprite("./images/enemy1.png", 2)# 创建敌机的精灵组enemy_group = pygame.sprite.Group(enemy, enemy1)# 游戏循环 -> 意味着游戏的正式开始while True: # 可以指定循环体内部的代码执行的频率 clock.tick(90) # 监听事件 for event in pygame.event.get(): # 判断事件类型是否是退出事件 if event.type == pygame.QUIT: print("游戏退出....") # quit 卸掉所有模块 pygame.quit() # exit() 直接终止正在运行的程序 exit() # 修改飞机的位置 hero_rect.y -= 1 # 判断英雄的位置 if hero_rect.bottom <= 0: hero_rect.y = 700 # 调用 blit 方法绘制图像 screen.blit(bg, (0, 0)) screen.blit(hero, hero_rect) # 让精灵组调用两个方法 # update - 让组中的所有精灵更新位置 enemy_group.update() # draw - 在 screen 上绘制所有的精灵 enemy_group.draw(screen) # 调用update方法更新显示 pygame.display.update() pass# 游戏的退出pygame.quit()