简介:这份Pygame教程面向已掌握Python基础、希望入门2D游戏开发的初学者与自学者,系统讲解游戏主循环、Surface对象、Event事件、Rect区域等核心概念,帮助读者从零搭建可运行的小游戏。教程以知识点配合示例的方式组织,覆盖Display显示模块、Transform图像变形、Time时间控制、Draw绘图函数、Font文本字体以及精灵与碰撞检测等模块,并穿插飞机大战、贪吃蛇、扫雷等典型场景,便于边学边练、理解各模块的协作方式。资源包为1个PDF文件,大小约1.32MB,单文件结构便于在电脑或移动设备上随时查阅,也方便按目录跳转到具体章节复习。目前已有1568人学习,适合作为游戏开发入门的系统读物,也可供有经验的开发者快速查阅Pygame常用接口与事件处理、帧率控制等细节。
1. 装 Pygame 之前,先把 Python 版本这关过了
上周帮一个做课程作业的同学看环境,他pip install pygame敲完,终端里红字刷了一屏,最后一行是error: Microsoft Visual C++ 14.0 or greater is required。他以为 Pygame 装不上是网络问题,反复重试了七八次。实际原因是他的 Python 是 3.12 刚发布那会儿装的,而当时 PyPI 上还没有对应 cp312 的预编译 wheel,pip 只能退回去找源码包自己编译,Windows 上又没有 C++ 编译链,于是直接翻车。这类问题在 pygame 课程资源的学习场景里特别常见——教程本身写得再细,环境不对,第一个import pygame就过不去。
Pygame 是 Python 的 2D 游戏开发模块,底层封装 SDL,跨 Windows、Linux、Mac,免费开源,飞机大战、贪吃蛇、扫雷这类练手项目基本都拿它起步。它不难,但安装环节的版本匹配、图形驱动、字体路径这几处,新手踩坑率极高。这篇记录按「选版本 → 装依赖 → 跑通第一个窗口 → 排错 → 验证」走一遍,适合刚接触游戏开发、手里有一份 pygame 课程资源想照着练的人。
2. 环境准备:Python 版本、pip 源与编译链三件事
2.1 Python 版本怎么选才不触发源码编译
Pygame 官方对 Python 版本的支持是跟着 wheel 走的。所谓 wheel,就是 PyPI 上提前编译好的二进制包,文件名里带cp37、cp311这种标记,对应 CPython 的具体小版本。你装的时候 pip 会先找匹配当前解释器的 wheel,找不到才退回.tar.gz源码包现场编译。所以选版本的核心逻辑只有一句:选一个 PyPI 上有现成 wheel 的 Python 小版本。
我一般的做法是,新机器上不追最新,装 Python 3.10 或 3.11 的最后一个补丁版。这两个版本线 Pygame 2.x 的 wheel 覆盖最全,Windows、macOS(Intel 和 Apple Silicon)、Linux 都有对应包。原文推荐 3.7 以上,这个下限没问题,但 3.7 已经停止维护,很多新系统上 pip 自身都跑不顺,实际落地建议 3.9 起步。
装完先确认解释器和 pip 是同一套,这一步很多人忽略,机器上装了多个 Python 时,pip和python可能指向不同位置:
# 确认 python 与 pip 指向同一解释器 python --version python -m pip --version # 输出里应能看到 python 的安装路径,两者目录要一致 # 例如 Windows 下都在 ...\Python310\ 下用python -m pip而不是裸pip,是让 pip 强制走当前python对应的那套环境,避免装到另一个解释器里去。这个习惯我从很早以前就固定下来了,多 Python 环境下能省掉大量「明明装了却 import 不到」的排查时间。
2.2 pip 源与虚拟环境:别往系统环境里直接灌
Pygame 包不大,但依赖链里有 SDL 相关的二进制,网络不稳时容易下到一半断掉。国内环境常见做法是配一个镜像源,临时用加-i参数即可,不必改全局配置:
# 创建并激活虚拟环境(Windows) python -m venv pygame-env pygame-env\Scripts\activate # macOS / Linux python3 -m venv pygame-env source pygame-env/bin/activate # 在虚拟环境内安装,指定镜像源 python -m pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple虚拟环境这一步不是形式主义。Pygame 的版本和 Python 小版本强绑定,一旦你以后升级了系统 Python,装在全局的 pygame 很可能失效,而项目里其他库又依赖旧版本,互相打架。用 venv 隔离,出问题直接删目录重建,成本极低。激活后命令行前面会出现(pygame-env)前缀,看到这个前缀再执行安装,能确认包落在正确位置。
2.3 Windows 上的编译链:什么时候才需要它
只有一种情况需要 Microsoft Visual C++ Build Tools:pip 找不到 wheel,退回去编译源码。判断方法很简单,看安装日志里有没有Building wheel for pygame这行。如果有,说明在编译,Windows 上大概率失败。
处理顺序是:先别急着装几个 G 的 Build Tools,回头检查 Python 版本是不是太新或太旧,换一个 wheel 覆盖全的版本重来,通常就解决了。确实需要编译链时(比如某些特殊平台),装 Visual Studio Build Tools 并勾选「使用 C++ 的桌面开发」即可。Linux 上对应的是python3-dev和libsdl2-dev这类开发头文件,Debian 系一条命令:
# Debian/Ubuntu 上补编译依赖,仅在需要源码编译时用 sudo apt install python3-dev libsdl2-dev libsdl2-image-dev \ libsdl2-mixer-dev libsdl2-ttf-dev这几个 SDL 子库分别对应 Pygame 的图像、声音、字体模块,缺哪个,对应功能在运行时就报NotImplementedError或直接初始化失败。装完再跑一次pip install pygame,日志里出现Successfully installed pygame-x.x.x就算过了。
3. 安装与首个窗口:从 pip 到能看见画面
3.1 两种安装方式的实际取舍
原文给了 pip 和二进制 whl 包两条路。pip 是首选,一条命令搞定,还能自动处理依赖。二进制包那条路适合完全离线的机器:去 PyPI 下载对应cpXX和平台标记的.whl文件,拷到目标机,用python -m pip install 文件名.whl装。注意 whl 文件名里的cp27m、win_amd64这些标记必须和你的解释器、系统架构严格对应,下错了 pip 会直接拒绝,报is not a supported wheel on this platform。
装完立刻验证版本,别等写代码时才发现装错环境:
# 查看已安装的 pygame 版本 python -m pygame --version # 再确认能正常导入 python -c "import pygame; print(pygame.version.ver)"两条命令都出结果,说明包本身没问题。如果第一条报No module named pygame,基本就是装到了别的解释器,回到 2.1 节用python -m pip重装。
3.2 第一个程序:初始化、Surface 与事件循环的最小骨架
Pygame 程序的骨架是固定的四段:初始化、建窗口、主循环、退出清理。下面这段是能直接跑的最小版本,我在每台新机器上都会先跑它确认图形环境通不通:
import sys import pygame # 初始化所有子模块:display、font、mixer 等 pygame.init() # 创建 400x400 的主窗口,返回的 screen 本质是一个 Surface screen = pygame.display.set_mode((400, 400)) pygame.display.set_caption("hello pygame") # 主循环:持续监听事件并刷新画面 while True: for event in pygame.event.get(): # 点击窗口右上角关闭时退出 if event.type == pygame.QUIT: pygame.quit() sys.exit() # 用灰色填充整个窗口 screen.fill((156, 156, 156)) # 把绘制内容刷到屏幕上 pygame.display.flip()pygame.init()会逐个初始化 display、font、mixer、cursors 等子模块,同时检测声卡、显示设备是否可用。它返回一个(成功数, 失败数)的元组,失败数不为 0 时不会抛异常,但后续用到对应模块会出问题,所以初始化后可以打印一下返回值看看。set_mode的size参数是窗口像素尺寸,flags可以传pygame.FULLSCREEN、pygame.RESIZABLE、pygame.NOFRAME等标志位组合,做全屏演示时用得上。
pygame.display.flip()和update()的区别值得记一下:flip整屏刷新,update可以只传一个矩形区域做局部刷新。小项目用哪个都行,画面元素多、想省性能时用update(rect)只刷变化区域。
3.3 文字渲染与字体路径:中文显示的第一个坑
原文示例里用了C:/Windows/Fonts/simhei.ttf这个绝对路径。这在 Windows 上能跑,但换台机器、换个系统就崩。更稳的写法是用pygame.font.SysFont按字体名找系统字体,或者用pygame.font.Font(None, size)走默认字体:
# 方式一:按系统字体名查找,跨平台更稳 font = pygame.font.SysFont("simhei", 50) # 方式二:传 None 用 Pygame 内置默认字体,不依赖系统字体文件 font = pygame.font.Font(None, 50) # 渲染文字:内容、是否抗锯齿、前景色、背景色 text = font.render("hello", True, (255, 0, 0), (0, 0, 0)) text_rect = text.get_rect() # 居中定位到 400x400 窗口的中心 text_rect.center = (200, 200) screen.blit(text, text_rect)render的第二个参数是抗锯齿开关,开着文字边缘平滑但略慢,关掉更快但锯齿明显,游戏里小字号文字一般开着。第四个参数背景色传None时背景透明,方便叠加到图片上。SysFont找不到指定字体名时不会报错,会静默回退到默认字体,所以中文显示成方块时,先确认字体名拼写和系统里是否真有这个字体,而不是怀疑渲染代码。
4. 模块配置:Display、Surface、Time 与 Event 的落地参数
4.1 Display 与 Surface:画布和贴图的关系
理解 Pygame 的关键是把screen当成一张最大的画布,所有内容都是先做成小 Surface,再用blit贴上去。pygame.Surface((w, h), flags, depth)可以新建一张空白 Surface,flags传pygame.HWSURFACE表示尽量放显存,传pygame.SRCALPHA则让每个像素带 alpha 通道,做半透明贴图时必须加。
从外部加载图片用pygame.image.load(path).convert(),convert()的作用是把图片像素格式转成和屏幕一致,之后 blit 会快很多。带透明通道的 PNG 要用convert_alpha(),用错的话透明区域会变成黑块。blit(source, dest, area, special_flags)里area传一个 Rect 可以做局部裁剪,special_flags控制混合模式,做发光、叠加效果时用BLEND_RGBA_ADD这类值。
pygame.transform负责变形:scale缩放、rotate旋转、rotozoom旋转加缩放一步到位。注意rotate是逆时针角度,且旋转后返回新 Surface,原图不变,尺寸也会变,做碰撞检测时要用旋转后的get_rect()重新取边界。
4.2 Time 模块:FPS 控制和定时器
游戏流畅度靠pygame.time.Clock控制。主循环里每帧调一次clock.tick(60),它会让这一帧最多等够 1/60 秒,从而把帧率压在 60 附近,同时返回上一帧实际耗时(毫秒):
clock = pygame.time.Clock() while True: # 限制最高 60 帧,返回上一帧耗时 dt = clock.tick(60) for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() # 用 dt 做与帧率无关的位移计算 # 例如速度 0.3 像素/毫秒,则每帧移动 0.3 * dt 像素 screen.fill((0, 0, 0)) pygame.display.flip()不调tick的话,循环会跑满 CPU,帧率飙到几百上千,动画速度完全不可控。pygame.time.get_ticks()返回从初始化到现在的毫秒数,做倒计时、技能冷却用它;pygame.time.wait(ms)是硬暂停,会阻塞整个程序,只在加载画面这种场景用,别放进主循环。
4.3 Event 与 Rect:交互和碰撞的基础
事件循环里pygame.event.get()一次取出当前队列里所有事件。键盘用event.type == pygame.KEYDOWN配合event.key == pygame.K_SPACE判断具体按键,鼠标用MOUSEBUTTONDOWN配合event.pos拿坐标。持续按住的状态不要靠 KEYDOWN 反复触发,用pygame.key.get_pressed()每帧查一次更准。
Rect是矩形区域对象,rect.colliderect(other)判断两个矩形是否相交,rect.collidepoint(x, y)判断点是否在矩形内。做精灵碰撞时,Rect 比逐像素检测快得多,绝大多数 2D 游戏够用。Rect的left、top、center、centerx这些属性可以直接赋值来定位,比手动算坐标省事。
5. 避坑排查:五条装完才暴露的典型问题
5.1 报ModuleNotFoundError: No module named 'pygame'
现象是命令行里pip install pygame显示成功,进 Python 一 import 就找不到。原因几乎都是 pip 和 python 不是同一套解释器,包装进了另一个 Python 的 site-packages。解决:用python -m pip install pygame重装,装完python -c "import pygame"验证。虚拟环境里则先确认命令行前缀是(pygame-env)。
5.2 窗口一闪而过,或者干脆不显示
现象是脚本跑完立刻退出,看不到窗口。原因是主循环写成了if或者循环体里没有持续刷新,程序执行完就结束了。Pygame 的窗口必须靠while True主循环撑着,循环里要有event.get()和display.flip()。另外在部分 IDE 的内置终端里跑图形程序会有显示异常,换系统终端执行更稳。
5.3 中文全是方块或问号
现象是render出来的中文显示成豆腐块。原因是字体文件里没有中文字形,或者SysFont指定的字体名系统里不存在,静默回退到了不含中文的默认字体。解决:Windows 用simhei、msyh,macOS 用pingfang、stheiti,Linux 先fc-list :lang=zh查可用中文字体名,再传给SysFont。实在不行用Font加载一个确定存在的中文 ttf 绝对路径。
5.4 声音模块初始化失败,报 mixer 相关错误
现象是pygame.init()返回的失败数不为 0,或者pygame.mixer.init()抛异常。原因是机器没有可用声卡,或者容器、远程环境里没有音频设备。解决:不需要声音时单独初始化pygame.display.init()和pygame.font.init(),跳过 mixer;需要声音但设备异常时,用pygame.mixer.init(frequency=44100, size=-16, channels=2)显式指定参数,避开自动探测。
5.5 图片加载报Couldn't open xxx
现象是pygame.image.load找不到文件。原因是相对路径的基准是「运行命令时所在的目录」,不是脚本所在目录。解决:用os.path.join(os.path.dirname(__file__), "assets", "bg.png")拼绝对路径,或者统一用pathlib.Path(__file__).parent / "assets" / "bg.png"。资源目录和脚本放一起,路径写死成相对脚本的绝对路径,换机器也不会崩。
6. 装完怎么验证:一套五分钟跑通的检查清单
装完 Pygame 别急着扎进教程正文,先花五分钟把下面这套检查跑一遍,能提前暴露九成的环境问题。我习惯把它存成一个check_pygame.py,每台新机器装完都执行一次。
import sys import pygame # 1. 版本与初始化 print("pygame version:", pygame.version.ver) ok, fail = pygame.init() print("init result:", ok, "failed:", fail) # 2. 显示设备是否可用 screen = pygame.display.set_mode((320, 240)) print("display driver:", pygame.display.get_driver()) # 3. 字体模块与中文支持 font = pygame.font.SysFont("simhei", 24) text = font.render("测试", True, (255, 255, 255)) print("font render size:", text.get_size()) # 4. 图像加载与格式转换 surf = pygame.Surface((50, 50), pygame.SRCALPHA) surf.fill((255, 0, 0, 128)) print("surface alpha:", surf.get_at((0, 0))) # 5. 时钟与事件队列 clock = pygame.time.Clock() clock.tick(60) print("event queue length:", len(pygame.event.get())) pygame.quit() print("all checks passed")逐项看输出:init result的 failed 数应为 0,不为 0 说明有子模块没起来,对照 5.4 处理;display driver在 Windows 上通常是windows,Linux 上是x11,如果是dummy说明跑在无显示环境里,图形窗口不会真正出现;字体那行get_size()返回的宽高不为 0,说明中文渲染链路通了;Surface 的 alpha 值读出来是 128 左右,说明透明通道生效;最后一行打印出来,整套环境就算齐活。
再补一个更直观的验证:跑一个带 FPS 显示的小窗口,看帧率是否稳定在设定值附近。如果clock.get_fps()长期远低于 60,要么机器性能吃紧,要么主循环里有阻塞操作(比如在循环里time.wait或者同步加载大图)。这个指标比任何日志都直接。
提示:验证脚本里用到的
simhei字体名只在 Windows 上保证存在,跨平台项目把它换成配置项,按系统分支取字体名,别写死在业务代码里。
从那以后我每次配新环境,都强制先跑一遍这套检查再动教程代码,宁可多花五分钟,也不在写业务逻辑时被环境问题带偏方向。Pygame 本身不复杂,翻车基本都翻在版本、路径、驱动这三处,把这三处钉死,后面 Surface、Event、精灵碰撞那些内容照着 pygame 课程资源练就行。希望帮到你。
本文还有配套的精品资源,点击获取