第一章、概述
一、pygame介绍
Pygame是一个利用SDL库写的游戏库,SDL全名:Simple DirectMedia Layer,是一位叫做Sam Lantinga的大牛写的。
SDL是用C写的,不过它也可以使用C++进行开发,当然还有很多其他的语言,Pygame就是python中使用它的一个库
二、主要功能
pygame
是一个用于开发 2D 游戏的 Python 第三方库。它提供了许多功能强大的工具和功能,使得创建游戏变得更加容易。以下是关于 pygame
的一些详细信息:
功能特性:
1、图形绘制
pygame
提供了用于绘制基本形状、图片、文本等的功能,使得游戏界面的设计变得简单。
2、事件处理
它支持对键盘、鼠标等用户输入事件的监听和处理,让开发者可以轻松地实现用户交互。
3、声音和音乐
pygame
具备处理声音和音乐的能力,能够加载、播放和控制游戏中的音频资源。
4、碰撞检测
提供了碰撞检测的功能,方便处理游戏中对象之间的碰撞情况。
5、动画与定时器
支持动画制作和定时器功能,让开发者能够实现游戏中的动态效果。
6、跨平台性
pygame
是跨平台的,可以在多个操作系统上运行。
三、官方资源:
第二章、安装库
一、安装
pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple
在pycharm的虚拟环境中安装pygame不成功的话,则先在系统环境中安装,从系统环境中直接将包拷贝到pycharm的虚拟环境中。
二、验证安装
python -m pygame.examples.aliens
若pygame中内置游戏aliens能够正常启动,则说明pygame模块已经被正确安装且能正常使用了
第三章、游戏初始化
游戏的第一印象:
1、把一些 静止的图像 绘制到 游戏窗口 中
2、根据 用户的交互 或其他情况,移动 这些图像,产生动画效果
3、根据 图像之间 是否发生重叠,判断 敌机是否被摧毁 等其他情况
可以将图片素材 绘制 到游戏的窗口上,开发游戏之前需要先知道 如何建立游戏窗口!
一、游戏的初始化和退出
- 要使用 pygame 提供的所有功能之前,需要调用 init 方法
- 在游戏结束前需要调用一下 quit 方法
1、初始化资源:pygame.init()
- 功能:
pygame.init()
方法用于初始化 Pygame 库,准备开始使用 Pygame 提供的功能和模块。 - 调用位置: 在使用 Pygame 前,通常需要在代码的开头调用这个方法。
- 初始化内容: 这个方法会初始化所有导入的模块,包括显示、声音、事件、时钟等。它会检查并初始化 Pygame 的各个模块,确保准备好开始使用 Pygame 提供的功能。
- 返回值: 一般情况下不需要接收返回值,如果初始化失败,可能会返回一个错误码或者抛出异常。
2、释放资源:pygame.quit()
- 功能:
pygame.quit()
方法用于退出 Pygame 库,释放资源并关闭 Pygame 使用的窗口和其他资源。 - 调用位置: 通常在程序结束时调用这个方法,确保资源得到释放。
- 关闭内容: 这个方法会释放 Pygame 使用的所有资源,包括显示、声音、事件等。它会关闭打开的窗口、释放申请的内存等资源。
- 注意事项: 在调用
pygame.quit()
之后,一般来说就不能再继续使用 Pygame 的其他功能了,除非重新调用pygame.init()
重新初始化 Pygame。
这两个方法是 Pygame 中很重要的初始化和退出函数,在使用 Pygame 开发游戏或应用时,通常在程序的开始和结束时分别调用这两个方法。
二、理解游戏中的坐标系
坐标系
原点 在 左上角(0, 0)
x轴 水平方向向 右,逐渐增加
y轴 垂直方向向 下,逐渐增加
在游戏中,所有可见的元素都是以 矩形区域 来描述位置的
要描述一个矩形区域有四个要素:(x, y)(width, height)
三、pygame.Rect:矩形区域类
pygame.Rect
是 Pygame 中用来表示矩形区域的类。它提供了对矩形进行各种操作和检测的方法pygame.Rect
是一个非常实用的类,用于处理游戏中的碰撞检测、位置控制等。通过这个类的方法和属性,可以方便地对矩形进行各种操作- pygame.Rect是一个比较特殊的类,内部只是封装了一些数字计算
- 不执行pygame.init()方法同样能够直接使用
1、创建矩形对象
使用坐标和尺寸创建
rect = pygame.Rect(x, y, width, height)
使用元组创建
rect = pygame.Rect((x, y), (width, height))
通过其他矩形对象创建
rect2 = rect1.copy() # 创建一个与 rect1 相同的矩形对象
2、常用属性和方法
x
,y
,width
,height
: 分别表示矩形的左上角坐标和宽度、高度。top
,bottom
,left
,right
: 分别表示矩形的上边缘、下边缘、左边缘、右边缘的坐标位置。centerx
,centery
: 表示矩形的中心点坐标。size
: 返回矩形的尺寸 (width, height)。colliderect()
: 用于检测两个矩形是否相交。collidepoint()
: 用于检测一个点是否在矩形内部。inflate()
: 扩大或缩小矩形的尺寸。move()
: 移动矩形。clamp()
: 限制矩形在另一个矩形范围内。
3、基本用法
# 创建矩形
rect = pygame.Rect(100, 100, 200, 150)
# 获取矩形属性
print(rect.x, rect.y) # 左上角坐标
print(rect.width, rect.height) # 宽度和高度
print(rect.centerx, rect.centery) # 中心点坐标
# 检测矩形相交
other_rect = pygame.Rect(150, 120, 100, 100)
if rect.colliderect(other_rect):
print("Rectangles collide!")
# 移动矩形
rect.move_ip(10, 20) # 直接修改矩形的位置
# 扩大矩形尺寸
rect.inflate_ip(20, 30) # 直接修改矩形的大小
四、pygame.display:创建游戏主窗口
pygame专门提供了一个 模块 pygame.display 用于创建,管理 游戏窗口
pygame.display
模块提供了管理游戏窗口和屏幕显示的功能。它包含了一些常用的函数和常量,用于控制显示窗口的属性、显示模式、刷新等操作。
以下是一些常用的 pygame.display
模块的函数和常量:
1、函数
set_mode((width, height), flags=0, depth=0)
:- 创建一个游戏窗口,并返回一个 Surface 对象,其中
width
和height
分别表示窗口的宽度和高度,flags
和depth
是可选参数,用于设置显示模式和颜色深度。
- 创建一个游戏窗口,并返回一个 Surface 对象,其中
set_caption(title)
:- 设置窗口标题,传入一个字符串作为标题内容。
flip()
或update()
:flip()
方法用于更新整个窗口,update()
方法也可以达到同样的效果,但可以传入参数来指定部分更新。
get_surface()
:- 获取当前窗口的 Surface 对象。
get_caption()
:- 获取当前窗口的标题。
toggle_fullscreen()
:- 切换全屏模式。
toggle_window()
:- 切换窗口模式。
1.1、pygame.display.set_mode()
初始化游戏显示窗口
set_mode(resolution=(0, 0), flags=0, depth=0) -> Surface
作用:创建游戏显示窗口
参数:
- resolution:指定屏幕的 宽 和 高,默认创建的窗口大小和屏幕大小一致
- flags:参数指定屏幕的附加选项,例如是否全屏等等,默认不需要传递
- depth:参数表示颜色的位数,默认自动匹配
返回值:
暂时可以理解为 游戏的屏幕,游戏的元素 都需要绘制到 游戏的屏幕上
注意:必须使用变量记录 set_mode()方法的返回结果!因为:后续所有的图像绘制都基于这个返回结果
2、常量
FULLSCREEN
: 用于设置全屏模式的标志。DOUBLEBUF
: 用于设置双缓冲模式的标志,能够有效地减少屏幕闪烁。HWSURFACE
和SWSURFACE
: 用于设置硬件加速和软件加速的标志。
3、基本用法
import pygame
from pygame import Surface
pygame.init()
# screen: Surface = pygame.display.set_mode()
# print(screen) # <Surface(1920x1080x32 SW)>
# print(screen.__str__()) # <Surface(1920x1080x32 SW)>
# 创建游戏的窗口 480 * 700
screen: Surface = pygame.display.set_mode((480, 700))
# 游戏循环
while True:
pass
pygame.quit()
pygame.display
模块提供了一些重要的功能来控制游戏窗口的显示模式、标题、全屏切换等操作。配合其他 Pygame 模块一起使用,可以实现游戏窗口的创建、更新和关闭等功能。
五、pygame.image:绘制图像
在游戏中,能够看到的 游戏元素 大多都是 图像
图像文件 初始是保存在磁盘上的,如果需要使用,第一步 就需要 被加载到内存
要在屏幕上 看到某一个图像的内容,需要按照三个步骤:
- 使用 pygame.image.load() 加载图像的数据
- 使用 游戏屏幕 对象,调用blit()方法,将图像绘制到指定位置
- 调用 pygame.display.update()方法更新整个屏幕
提示:要想在屏幕上看到绘制的结果,就一定要调用 pygame.display.update()方法
需求:
1、加载 me1.png 创建英雄飞机
2、将 英雄飞机 绘制在屏幕的 (200, 500)位置
3、调用屏幕更新显示飞机图像
# 绘制英雄的飞机
# 1、load:加载图像
hero: Surface = pygame.image.load("../images/me1.png")
# 2、blit:绘制在屏幕
screen.blit(hero, (200, 500))
# 3、update:更新屏幕显示
pygame.display.update()
1、透明图像
透明图像:
.png 格式的图像是支持 透明 的
在绘制图像时,透明区域 不会显示任何内容
但是如果下方已经有内容,会 透过 透明区域 显示出来
2、理解 update() 方法的作用
可以在 screen 对象完成 所有 blit 方法之后,统一调用一次 display.update 方法,同样可以在屏幕上 看到最终的绘制对象
- 使用 pygame.display.set_mode() 创建的screen 对象 是一个 内存中的屏幕数据对象
- 可以理解成是 油画 的 画布
- screen.blit() 方法可以在 画布 上绘制很多图像
- 例如:英雄,敌机, 子弹......
- 这些图像 有可能会彼此的 重叠或覆盖
- pygame.display.update()方法会将 画布 的最终结果 绘制到屏幕上,这样可以 提高屏幕绘制效率,增加游戏的流畅度
六、设置游戏时钟
pygame专门提供了一个类 pygame.time.Clock 可以非常方便的设置屏幕绘制速度 (刷新帧率)
要使用 时钟对象 需要两步:
- 在 游戏初始化 创建一个 时钟对象
- 在 游戏循环 中让时钟对象调用 tick(帧率) 方法
tick 方法会根据 上次被调用的时间,自动设置 游戏循环 中的延时
# 创建时钟对象
clock = pygame.time.Clock()
# 游戏循环 -》 意味着游戏的正式开始
i: int = 0
while True:
# 帧率60,每秒钟执行60次
# 可以指定循环体内部的代码执行的频率
clock.tick(60)
print(i)
i += 1
第四章、游戏循环
一、概述
为了做到游戏程序启动后,不会立即退出,通常会在游戏程序中增加一个 游戏循环
所谓的 游戏循环 就是一个 无限循环
在 创建游戏窗口 代码下方,增加一个无限循环
注意:游戏窗口不需要重复创建
import pygame
# 创建游戏主窗口
screen = pygame.display.set_mode((480, 700))
# 游戏循环
while True:
pass
1、游戏组成两部分:游戏初始化,游戏循环
2、游戏循环的作用
保证游戏 不会直接退出
变化图像位置:动画效果
- 每隔 1/60 秒 移动一下所有图像的位置
- 调用 pygame.display.update() 更新屏幕显示
检测用户交互:按键,鼠标等......
二、游戏循环中的 监听 事件
1、事件
事件 event
就是游戏启动后,用户针对游戏所做的操作
例如:点击关闭按钮,点击鼠标,按下键盘
2、监听
在 游戏循环 中,判断用户 具体的操作
只有 捕获 到用户具体的操作,才能有针对性的做出响应
3、代码实现
pygame 中通过 pygame.event.get() 可以获得 用户当前所做动作 的 事件列表
用户可以同一时间做很多事情
提示:这段代码非常的固定,几乎所有的 pygame 游戏都大同小异
import sys
import pygame
# 初始化pygame
pygame.init()
# 游戏初始化
# 设置游戏窗口
screen = pygame.display.set_mode((480, 700))
# 设置游戏窗口标题
pygame.display.set_caption("飞机大战")
# 绘制图像初始位置
background = pygame.image.load("../images/background.png")
screen.blit(background, (0, 0))
hero = pygame.image.load("../images/me1.png")
screen.blit(hero, (150, 300))
# 设置游戏时钟
clock = pygame.time.Clock()
# 1、定义rect记录飞机的初始位置
hero_rect = pygame.Rect(150, 300, 102, 126)
# 游戏循环
running = True
while running:
clock.tick(60)
# 捕获事件
event_list = pygame.event.get()
if event_list.__len__() > 0:
print(event_list)
# 遍历事件列表
for event in event_list:
# 判断事件类型是否为退出事件
if event.type == pygame.QUIT:
print("游戏退出...")
# quit 卸载所有的模块
pygame.quit()
# 退出进程
sys.exit(0)
# 2、修改飞机的位置
hero_rect.y -= 1
# 判断飞机的位置
if hero_rect.y <= 0:
hero_rect.y = 574
# 3、调用blit方法绘制图像
screen.blit(background, (0, 0))
screen.blit(hero, hero_rect)
# 4、调用update方法更新显示
pygame.display.update()
# 退出pygame
pygame.quit()
二、案例
1、英雄的简单动画实现
需求:
- 在 游戏初始化 顶一个pygame.Rect 的变量记录英雄的初始位置
- 在 游戏循环 中每次让 英雄 的 y - 1 ,向上移动
- y <= 0 时,将英雄移动到屏幕的底部
提示:
每一次调用 update() 方法之前,需要把 所有的游戏图像都重新绘制一遍
而且应该 最先 重新绘制背景图片
import pygame
# 初始化pygame
pygame.init()
# 游戏初始化
# 设置游戏窗口
screen = pygame.display.set_mode((480, 700))
# 设置游戏窗口标题
pygame.display.set_caption("飞机大战")
# 绘制图像初始位置
background = pygame.image.load("../images/background.png")
screen.blit(background, (0, 0))
hero = pygame.image.load("../images/me1.png")
screen.blit(hero, (150, 300))
# 设置游戏时钟
clock = pygame.time.Clock()
# 1、定义rect记录飞机的初始位置
hero_rect = pygame.Rect(150, 300, 102, 126)
# 游戏循环
running = True
while running:
clock.tick(60)
# 2、修改飞机的位置
hero_rect.y -= 1
# 判断飞机的位置
if hero_rect.y <= 0:
hero_rect.y = 700
# 3、调用blit方法绘制图像
screen.blit(background, (0, 0))
screen.blit(hero, hero_rect)
# 4、调用update方法更新显示
pygame.display.update()
# 退出pygame
pygame.quit()
第五章、 精灵和精灵组
一、概述
在上述的案例中,图像加载,位置变化,绘制图像 都需要程序员编写代码分别处理
为了简化开发步骤,pygame提供了两个类
- pygame.sprite.Sprite 存储 图像数据 image 和 位置rect 的对象
- pygame.sprite.Group
1、精灵:pygame.sprite.Sprite(需要派生子类)
- image:记录图像数据
- rect:记录在屏幕上的位置
- update(*args):更新精灵位置
- kill():从所有组中删除
2、精灵组:pygame.sprite.Group
- __init__(self, *精灵)
- add(*sprites):向组中增加精灵
- sprites():返回所有精灵列表
- update(*args):让组中所有精灵调用 update 方法
- draw(Surface):将组中所有精灵的 image,绘制到Surface 的rect 位置
二、派生精灵子类
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() 方法,可以返回 pagame.Rect(0, 0, 图像宽, 图像高) 的对象
import pygame
class PlaneSprites(pygame.sprite.Sprite):
"""
飞机大战游戏精灵
"""
def __init__(self, image_name, speed=1) -> None:
# 调用父类的构造初始化方法
super().__init__()
# 定义对象的属性
self.image = pygame.image.load(image_name)
self.rect = self.image.get_rect()
self.speed = speed
def update(self) -> None:
# 在屏幕的垂直方向上移动
self.rect.y += self.speed
2、使用 游戏精灵 和 精灵组 创建敌机
需求:
使用 游戏精灵派生类 和 精灵组 创建 敌机,并且实现敌机动画
步骤:
1、使用from 导入 plane_sprites 模块中的PlaneSprites类
- from 导入的模块可以直接使用
- import 导入的模块需要通过 模块名. 来使用
2、在 游戏初始化 创建 精灵对象 和 精灵组 对象
3、在 游戏循环中 让 精灵组 分别调用 update() 和 draw(screen) 方法
职责:
- 精灵
- 封装 图像image,位置rect 和 速度 speed
- 提供 update() 方法,根据游戏需求,更新位置 rect
- 精灵组
- 包含多个 精灵对象
- update方法,让精灵组中的所有精灵调用 update 方法更新位置
- draw(screen) 方法,在screen上绘制精灵组中的所有精灵
import sys
from typing import Any
import pygame
from pygame import Surface
import plane_sprites
# 初始化pygame
pygame.init()
# 创建游戏窗口对象
screen: Surface = pygame.display.set_mode((480, 700))
# 设置窗口标题
pygame.display.set_caption("飞机大战")
# 初始化图像位置
background = pygame.image.load("../images/background.png")
screen.blit(background, (0, 0))
hero = pygame.image.load("../images/me1.png")
screen.blit(hero, (150, 300))
# 创建 时钟对象
clock = pygame.time.Clock()
rect = pygame.Rect(150, 300, 102, 126)
# 创建敌机的精灵
enemy: Any = plane_sprites.GameSprite("../images/enemy1.png")
enemy1: Any = plane_sprites.GameSprite("../images/enemy1.png", 2)
# 创建敌机的精灵组
enemy_group = pygame.sprite.Group()
enemy_group.add(enemy)
enemy_group.add(enemy1)
running = True
while running:
clock.tick(60)
event_list = pygame.event.get()
for event in event_list:
if event.type == pygame.QUIT:
print("游戏退出...")
pygame.quit()
sys.exit(0)
rect.y -= 1
if rect.y <= 0:
rect.y = 574
screen.blit(background, (0, 0))
screen.blit(hero, rect)
# 让精灵组调用两个方法
# update 让组中的所有精灵更新位置
enemy_group.update()
# draw 将精灵组中的所有精灵绘制到屏幕时上
enemy_group.draw(screen)
# 更新屏幕的显示
pygame.display.update()
# 退出pygame
pygame.quit()
第六章、游戏框架搭建
一、主游戏类搭建
二、游戏配置文件设计
使用 常量 代替固定的数值
- 常量 -- 不变化的量
- 变量 -- 可以变化的量
应用场景:
- 在开发时,可能会需要使用 固定的数值,例如 屏幕的高度 是 700
- 这个时候,建议 不要 直接使用固定数值,而应该使用 常量
- 在开发时,为了保证代码的可维护性,尽量不要使用 魔法数字
常量的定义
- 定义 常量 和定义 变量 的语法完全一样,都是使用 赋值语句
- 常量 的命名应该 所有字母都使用大写,单词与单词之间使用下划线连接
常量的好处
- 阅读代码时,通过 常量名 见名知意,不需要猜测数字的含义
- 如果需要 调整值,只需要 修改常量定义 就可以实现 统一修改
提示:python中并没有真正意义上的常量,只是通过命名的约定 -- 所有字母都是大写的就是常量,开发时不要轻易的修改
三、游戏背景
背景交替滚动的思路确定
游戏启动后,背景图像 会 连续不断地向下方 移动
在 视觉上 产生英雄的飞机不断向上方飞行的 错觉 -- 在很多跑酷类游戏中常用的套路
- 游戏的背景 不断变化
- 游戏的主角 位置保持不变
实现思路:
1、设计背景类
初始化方法
- 直接指定 背景图片
- is_alt 判断是否为另一张图像
- False:表示 第一张图像,需要与屏幕重合
- True:表示 另一张图像,在屏幕的正上方
- update() 方法
- 判断 是否移动出屏幕,如果是,将图像设置到 屏幕的正上方,从而实现 交替滚动
继承:如果父类提供的方法,不能满足子类的需求
- 派生出一个子类
- 在子类中针对特有的需求,重写父类的方法,并且进行扩展
2、在main.py 中显示背景精灵
在 __create_sprites 方法中创建 精灵 和 精灵组
在 __update_sprites 方法中,让 精灵组 调用 update() 和 draw() 方法
__create_sprites方法
def __create_sprites(self):
"""
创建游戏精灵
:return:
"""
# 创建精灵组
self.sprite_group = pygame.sprite.Group()
# 创建背景精灵1
self.background1: Any = background_sprites.BackgroundSprites(game_config.IMAGE_BACKGROUND)
# 创建背景精灵2
self.background2: Any = background_sprites.BackgroundSprites(game_config.IMAGE_BACKGROUND)
# 修改背景精灵2的初始位置
self.background2.rect.y = -self.background2.rect.height
# 将创建的游戏精灵添加进精灵组中
self.sprite_group.add(self.background1, self.background2)
__update_sprites方法
# self.rect.y -= 1
# if self.rect.y <= 0:
# self.rect.y = 574
# self.screen.blit(self.background, (0, 0))
# self.sprite_group.add(self.enemy1, self.enemy2)
四、定时器
使用 定时器 添加敌机
设计 Enemy 类
1、使用定时器添加敌机
- 游戏启动后,每隔1秒 会出现一架敌机
- 每架敌机 向屏幕下方飞行,飞行速度各不相同
- 每架敌机出现的水平位置也不尽相同
- 当敌机 从屏幕下方飞出,不会在飞回到屏幕中
2、定时器
在 pygame 中可以使用 pygame.time.set_timer() 来添加 定时器
所谓 定时器 ,就是 每隔一段时间 ,去执行一些动作
def set_timer(event: Union[int, Event], millis: int, loops: int = 0) -> None: ...
- set_timer:可以创建一个 事件
- 可以在 游戏循环 的 事件监听 方法中捕获到该事件
- 第1个参数 事件代号 需要基于常量 pygame.USEREVENT 来指定
- USEREVENT 是一个整数,在增加的事件可以使用 USEREVENT + 1指定,依次类推
- 传入的参数 事件ID 必须比USEREVENT大,不能比这个数小,因此只能加
- 第2个参数是 事件触发 间隔的 毫秒值 1秒 = 1000毫秒
3、定时器事件的监听
- 通过 pygame.event.get() 可以获取当前时刻所有的事件列表
- 遍历列表 并且判断 event.type 是否等于 eventid,如果相等,表示 定时器事件 发生
4、定义并监听创建敌机的定时器事件
pygame的定时器 使用套路非常固定:
- 定义 定时器常量 (eventid)
- 在 初始化方法 中,调用 set_timer 方法,设置定时器事件
- 在 游戏循环 中,监听定时器事件
五、设计Enemy类
- 游戏启动后,每隔1秒 会出现一架敌机
- 每架敌机 向屏幕下方飞行,飞行速度各不相同
- 每架敌机出现的水平位置也不尽相同
- 当敌机 从屏幕下方飞出,不会在飞回到屏幕中
初始化方法:
- 指定 敌机图片
- 随机 敌机的 初始位置 和 初始速度
重写 update() 方法
- 判断 是否飞出屏幕,如果是,从 精灵组 删除
1、创建敌机
- 在 __create_sprites,添加 敌机精灵组
- 敌机是 定时被创建的,因此在初始化方法中,不需要创建敌机
- 在__event_handler,创建敌机,并且 添加到精灵组
- 调用 精灵组 的 add 方法可以 向精灵组中添加精灵
- 在 __update_sprites,让 敌机精灵组 调用 update和draw方法
2、随机敌机位置和速度
随机速度
导入模块 random
在导入模块时,建议 按照以下顺序导入
- 官网标准模块导入
- 第三方模块导入
- 应用程序模块导入
随机位置
3、移出屏幕销毁敌机
- 敌机移出屏幕之后,如果 没有撞到英雄,敌机的历史使命已经终结
- 需要从 敌机组 删除,否则会造成 内存浪费
检测敌机被销毁
__del__内置方法会在对象被销毁前调用,在开发中,可以用于判断对象是否被销毁
六、设计Hero类
需求:
游戏启动后,英雄 出现在屏幕的 水平中间 位置,距离 屏幕底部 120 像素
英雄 每隔0.5秒发射一次子弹,每次连发三枚子弹
英雄默认不会移动,需要通过 左/右 方向键,控制 英雄 在水平方向移动
1、 初始化方法
指定 英雄图片
初始速度 为 0 英雄默认静止不动
定义 bullets 子弹精灵组 保存子弹精灵
2、重写update() 方法
英雄需要 水平移动
并且需要保证不能移出屏幕
增加bullets 属性,记录所有子弹精灵
增加fire方法,用于发射子弹
3、移动英雄位置
在pygame 中针对 键盘按键的捕获,有 两种 方式
第一种方式:
- 判断 event.type == pygame.KEYDOWN
event.type == pygame.KEYDOWN and event.key == pygame.K_RIGHT
第二种方式:
- 首先使用 pygame.key.get_pressed() 返回 所有按键元祖
- 通过 键盘常量,判断元祖中 某一个键是否被按下,如果被按下,对应数值为1
# 使用键盘提供的方法获取键盘按键
keys_pressed: tuple[Any, ...] = pygame.key.get_pressed()
# 判断元祖中对应的按键索引值
if keys_pressed[pygame.K_RIGHT]:
print(keys_pressed[pygame.K_RIGHT])
print("右移")
两种方式的区别:
- 第一种方式:event.type 用户 必须要抬起按键 才算一次 按键事件,操作灵活性会大打折扣
- 第二种方式:用户可以按住方向键不放,就能够实现持续向某一个方向移动了,操作灵活性更好
在 Hero 类中重写 update方法
- 用速度speed 和英雄 rect.x 进行叠加
- 不需要调用父类方法 -- 父类方法只是实现了单纯的垂直运动
在__event_handler 方法中更具 左右方向键 设置英雄的 速度
- 向右 --》 speed = 2
- 向左 --》 speed = -2
- 其他 --》 speed = 0
七、设计子弹类
子弹 从 英雄 的正上方发射 沿直线 向 上方飞行
飞出屏幕后,需要从 精灵组 中删除
1、初始化方法
指定子弹图片
初始速度 = 2 子弹需要向上方飞行
2、重写update()方法
判断是否飞出屏幕,如果是,从精灵组 删除
3、发射子弹
- 在Hero的初始化方法中创还能 子弹精灵组 属性
- 修改main.py的__update_sprites方法,让 子弹精灵组 调用 update和draw方法
- 实现fire()方法
- 创建子弹精灵
- 设置初始位置 -- 在英雄的正上方
- 将 子弹 添加到精灵组
八、碰撞检测
1、碰撞检测方法
pygame.sprite.groupcollide()
两个精灵组 中 所有的精灵 的碰撞检测
def groupcollide(
groupa: AbstractGroup[_TSprite],
groupb: AbstractGroup[_TSprite2],
dokilla: bool | Literal[1] | Literal[0],
dokillb: bool | Literal[1] | Literal[0],
collided: Optional[Callable[[_TSprite, _TSprite2], bool]] = None,
) -> Dict[_TSprite, List[_TSprite2]]: ...
- 如果将 dokill 设置为 True,则 发生碰撞的精灵将被自动移动
- collided 参数是用于 计算碰撞的回调函数
- 如果没有指定,则每个精灵必须有一个rect属性
pygame.sprite.spritecollide()
判断 某个精灵 和 指定精灵组 中的精灵的碰撞
def spritecollide(
sprite: _HasRect,
group: AbstractGroup[_TSprite],
dokill: bool | Literal[1] | Literal[0],
collided: Optional[Callable[[_HasRect, _TSprite], bool]] = None,
) -> List[_TSprite]: ...
- 如果将 dokill 设置为 True,则 指定精灵组中发生碰撞的精灵将被自动移除
- collided 参数是用于 计算碰撞的回调函数
- 如果没有指定,则每个精灵必须有一个rect属性
- 返回 精灵组 中跟 精灵 发生碰撞的 精灵列表
第七章、相关问题
一、pygame: libpng warning: iCCP问题
示例用法:
python import pygame # 初始化 Pygame pygame.init() # 创建窗口 width = 800 height = 600 screen = pygame.display.set_mode((width, height)) pygame.display.set_caption("My Game") # 游戏循环 running = True while running: for event in pygame.event.get(): if event.type == pygame.QUIT: running = False # 绘制游戏元素等操作 # 刷新屏幕显示 pygame.display.flip() # 退出 Pygame pygame.quit()
基本用法:
import pygame
import sys
pygame.init()
# 设置窗口大小
width, height = 800, 600
screen = pygame.display.set_mode((width, height))
pygame.display.set_caption('My Game')
# 游戏主循环
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# 绘制游戏界面
pygame.display.flip()
pygame.quit()
sys.exit()
注意事项:
pygame
是一个强大的工具,但并不是专门为复杂游戏设计的。对于大型游戏项目,可能需要额外的游戏引擎或框架。- 使用
pygame
前需要确保已经正确安装,并了解其基本概念和功能,以充分利用其提供的功能。