pygame 入门
pygame 是一个基于 SDL 的 python 游戏框架,使用它可以制作二维游戏和多媒体应用。
目前的最新版本是 2.6.0,发布于 2024 年 6 月 25 日。安装命令如下:
# 复杂形式
$ python3 -m pip install -U pygame==2.6.0
# 简单形式
$ pip3 install pygame
pygame 附赠了许多游戏例子,路径位于 pygame/examples
中。如果想验证安装是否正常,可以执行如下代码,这会启动一个外星人入侵的小游戏:
$ python3 -m pygame.examples.aliens
基本概念
- Surface(表面):Surface 是绘制图像的二维平面。使用
pygame.display.set_mode(size)
创建的Surface
是游戏主窗口 - Rect(矩形):Rect 表示二维矩形,游戏元素的定位、碰撞检测都离不开它
- Sprite(精灵):Sprite 表示位置可以变化的游戏元素
- Group(群组):Group 是
Sprite
的容器,方便批量管理和渲染多个Sprite
- 帧率:指的是每秒播放多少帧,帧率越快,画面越平滑,同时也更加消耗 CPU。通常游戏的帧率设定为 60fps。在 pygame 中,使用 pygame.time.Clock 实例控制帧率
游戏主循环
pygame 游戏的基本框架包括初始化和游戏主循环,主要的游戏逻辑在主循环中定义。
import pygame
# 初始化所有模块
pygame.init()
# 设定窗口尺寸
screen = pygame.display.set_mode((1280, 720))
# 创建计时器,用于限定帧率
clock = pygame.time.Clock()
running = True
while running:
# 监听事件
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill('purple')
# Render your game here
# 更新屏幕画面
pygame.display.flip()
clock.tick(60) # 限制帧率为 60fps
# 卸载所有模块
pygame.quit()
颜色
使用 pygame.Color
类定义颜色。颜色可以只包含 RGB 三原色,也可以包含 RGBA,其中 A 表示透明度 alpha。每个通道的取值范围都是 [0, 255]
。
red = pygame.Color(255, 0, 0)
另外,pygame 还提供了命名颜色(named colors),比如 white
、black
、aqua
、azure
等常见颜色。
绘制图形
pygame.draw
模块提供了绘制基本几何图形的功能。比如:
line(surface, color, start_pos, end_pos, width=1)
绘制直线aaline(surface, color, start_pos, end_pos)
绘制抗锯齿的直线lines(surface, color, closed, points)
绘制折线aalines(surface, color, closed, points)
绘制抗锯齿的折线rect(surface, color, rect)
绘制矩形polygon(surface, color, points)
绘制多边形circle(surface, color, center, radius)
绘制圆形ellipse(surface, color, rect)
绘制椭圆arc(surface, color, rect, start_angle, stop_angle)
绘制椭圆圆弧,角度单位是弧度
加载图像
使用 pygame.image.load()
加载图像素材。返回值是 Surface 类型。
默认情况下,pygame 至少支持 .bmp
图片格式。如果 pygame.image.get_extended()
返回值为 True
,则表示可以加载更多图像格式,比如 .jpg
, .png
, .gif
等。
if not pygame.image.get_extended():
raise SystemExit('Sorry, extended image module required')
def load_image(file):
try:
surface = pygame.image.load(file)
except pygame.error:
raise SystemExit(f'Could not load image "{file}" {pygame.get_error()}')
return surface.convert()
上面的 convert()
函数用于转换图像的像素格式(pixel format),也就是把图像文件的原始格式转换为 Surface 支持的格式。提前转换像素格式,可以提高应用性能。对于包含透明度通道的图像格式,如 .png
等,可以使用 convert_alpha()
函数。
图像加载完毕,使用 Surface.blit()
将其绘制到屏幕上。
screen = pygame.display.set_mode((550, 400))
ship = load_image('ship.png')
ship_rect = ship.get_rect()
while True:
# ...
screen.blit(ship, ship_rect) # 绘制图像
绘制文本
绘制文本需要使用 pygame.font
模块。
绘制文本前先创建字体。创建字体有两种办法,一种从系统字体中创建,一种从字体文件中创建。
系统字体
首先使用 pygame.font.SysFont(name, size) -> Font
从系统字体中创建字体对象。
然后,使用 Font.render(text, antialias, color, background=None) -> Surface
将文字渲染为 Surface 表面。
最后,使用 screen.blit(surface, rect)
绘制到屏幕。
font = pygame.font.SysFont('verdana', 48)
text = font.render('Hello, Font!', True, 'red', 'green')
screen.blit(text, text.get_rect())
如果想显示中文,需要使用中文字体。执行 pygame.font.get_fonts()
可以查看所有字体。
字体文件
使用 pygame.font.Font()
函数从字体文件中创建字体。
font = pygame.font.Font('lxgw.ttf', 48)
text = font.render('得分', True, 'red')
screen.blit(text, text.get_rect())
sprite 模块
pygame 1.3 引入的 pygame.sprite
模块,用于处理移动的游戏物体。它包含两个主要的类:Sprite
和 Group
。
Sprite
使用 Sprite
的做法一般是:
- 创建自定义类,使其继承
Sprite
类 - 在自定义构造函数中定义两个属性:
image
(精灵的外观)和rect
(精灵的位置) - 覆盖父类方法
update()
,改变精灵外观或位置
from pygame import Sprite
class Alien(Sprite):
def __init__(self, *group):
super().__init__(*group)
self.image = load_image('images/alien.png')
self.rect = self.image.get_rect()
def update(self):
# 每次更新,垂直向下移动一个像素
self.rect.move_ip(0, 1)
Sprite
的常用方法包括:
update()
控制精灵外观的更新方法,需要在子类中覆盖。当执行Group.update()
时,群组内所有精灵的update()
方法均会被自动调用add(*groups)
添加精灵至指定群组remove(*groups)
从群组中移除精灵kill()
从所有群组中移除精灵alive() -> bool
判断精灵是否属于任何群组groups() -> group_list
返回所有包含此精灵的群组
Group
Group
类用于容纳和渲染 Sprite
,它的常用方法有:
add(*sprites)
在当前群组添加多个Sprite
实例remove(*sprites)
移除多个Sprite
实例update()
会调用群组内的所有Sprite
实例的update()
方法draw(surface)
在surface
表面绘制群组内所有精灵,使用Sprite
的image
和rect
当作外观和位置
import pygame
from pygame.sprite import Sprite, Group
pygame.init()
screen = pygame.display.set_mode((550, 400))
clock = pygame.time.Clock()
aliens = Group() # 新建 Group 群组
# 向群组添加 10 个 Sprite
for i in range(10):
new_alien = Alien()
new_alien.rect.move_ip(i * 60, 0)
aliens.add(new_alien)
# 游戏主循环
while True:
# ...
aliens.update() # 更新所有 Sprite 的外观和位置信息
aliens.draw(screen) # 渲染所有的 Sprite
pygame.display.flip() # 重绘整个画面
clock.tick(60)
碰撞检测
碰撞检测是 sprite 模块提供的一个重要功能。主要函数如下:
spritecollide(sprite, group, dokill) -> Sprite_list
检测精灵和群组是否发生碰撞,返回值是和精灵碰撞的群组内精灵组成的列表。dokill
是布尔值,如果为True
,发生碰撞的精灵会从群组中移除spritecollideany(sprite, group) -> Sprite
测试精灵是否和群组发生碰撞,比spritecollide()
函数稍快collide_rect(left, right) -> bool
基于矩形的精灵碰撞检测,要求两个精灵实例必须包含rect
属性collide_rect_ratio(ratio) -> collided_callable
基于缩放矩形的精灵碰撞检测collide_circle(left, right) -> bool
基于圆形的精灵碰撞,要求两个精灵拥有rect
属性,可以拥有radius
属性collide_circle_ratio(ratio) -> collided_callable
基于缩放圆形的碰撞检测collide_mask(sprite1, sprite2) -> (int, int)
基于遮罩的碰撞检测groupcollide(group1, group2, dokill1, dokill2) -> Sprite_dict
两个群组之间的碰撞检测
声音
游戏中的声音分两种,一种是背景音乐,一种是动作音效。
背景音乐一般较长,循环反复播放,用于营造游戏气氛。动作音效相对短促,比如爆炸、碰撞等声音,常常和瞬时事件绑定,不需要长时间播放。
pygame 使用两个不同的模块分别处理背景音乐和动作音效。
背景音乐
pygame.mixer.music
模块提供了背景音乐的播放功能。背景音乐使用流媒体方式加载,即一边下载一边播放,有较短的启动速度。
较早的 pygame 在 Mac 和 Linux 平台上对于 MP3 格式的支持有限,pygame v2.0.2 增强了 MP3 的支持。如果你需要更好的压缩效果,可以使用 OGG 格式代替 MP3。
背景音乐只能播放一个文件,如果打开另一个音乐文件,当前播放的音乐会自动停止。
播放背景音乐的一般步骤如下:
- 执行
load(filename)
方法加载音乐文件 - 执行
set_volume(volume)
设定音量,音量volume
是[0.0, 1.0]
区间的浮点数 - 执行
play(loops)
方法播放文件,可以指定循环次数。默认是0
,表示不重复播放。设为-1
,表示无限循环播放
举个例子,按 P 键播放音乐,按 S 键停止音乐:
import pygame as py
py.init()
screen = py.display.set_mode((550, 400))
# 加载背景音乐
py.mixer.music.load('sound/bgmusic.mp3')
running = True
while running:
for event in py.event.get():
if event.type == py.QUIT:
running = False
if event.type == py.KEYDOWN:
if event.key == py.K_q:
running = False
# 按 P 键,播放音乐
elif event.key == py.K_p:
py.mixer.music.play(-1) # 无限循环播放
# 按 S 键,停止播放音乐
elif event.key == py.K_s:
py.mixer.music.stop()
screen.fill('purple')
py.display.flip()
动作音效
使用 pygame.mixer.Sound()
加载动作音效。它的 play()
方法播放音效。
动作音效可以在多个通道上同时播放多个音效。常见的函数如下:
sound = pygame.mixer.Sound('sound.mp3') # 加载音效
sound.set_volume(0.2) # 设定音效音量
sound.play() # 播放音效
sound.stop() # 停止音效
未完待续