如果你最近也在搜索引擎里敲过"Pygame"这个关键词,大概率是刷到了某个用几百行代码做出小游戏的视频,或者正琢磨着周末搞点小东西练练手。我当年入坑Pygame,不是因为我有多热爱游戏开发,而是想给家里孩子做一个能算加减法的小游戏。跑了一圈游戏引擎,要么安装包大得吓人,要么学习曲线陡得劝退,最后反而在Pygame里找到了"你敲什么,屏幕上立刻就能看到什么"的踏实感。
这篇文章不打算讲什么高深的游戏架构,而是把从装环境到做完一个能玩的小游戏、再踩坑排错的全过程,按真实顺序拆开给你看。无论你是刚学Python、正在找练手项目的新手,还是想快速验证某个玩法原型的开发者,这篇都值得往下读——里面没有绕弯子的大道理,全是我实际操作时验证过的东西。
1. 为什么都2024年了,我还在用Pygame写小游戏
1.1 Pygame是什么,它站在谁的肩膀上
Pygame是一个基于Python的2D游戏开发库,底层是SDL(Simple DirectMedia Layer)的Python封装。SDL是一套跨平台的多媒体开发库,负责跟操作系统底层的图形窗口、音频设备、键盘鼠标输入打交道。换句话说,Pygame帮你把"打开窗口画像素"、"播放声音"、"读取按键"这些脏活累活全包了,你只需关心游戏逻辑本身。
这里有一个容易被忽略的点:Pygame不是游戏引擎,没有场景编辑器、物理引擎、动画状态机这些现成的东西。它更像是一个"画布+工具包"。你要自己写游戏循环、自己管理精灵、自己算碰撞。听起来麻烦,但好处是——每一行代码在干什么,你心里一清二楚。对学习游戏开发的人来说,这种"裸奔"反而是最宝贵的训练。
1.2 它擅长什么,又做不了什么
先泼一盆冷水。Pygame适合做2D小游戏、迷你游戏、教育类互动程序、游戏开发入门练手,以及快速验证玩法原型。但它不适合做大型3D游戏、不适合做需要极致性能优化的重度游戏,也不适合直接发布到手机应用商店。
具体来说:3D就别想了,Pygame本身只有2D绘制能力;移动端打包虽然能通过Kivy之类的方案曲线实现,但流程繁琐,体验也一般;同屏物体数量上千之后,纯Python绘制的性能瓶颈会非常明显。我见过有人硬要用Pygame做RTS游戏,最后卡在性能优化上怀疑人生。选工具先看边界,这是做项目最朴素的道理。
1.3 选择Pygame而不是其他引擎的真实理由
很多人会问:既然有Godot、Unity这么成熟的引擎,为什么还要用Pygame?我的回答是:目的不同。如果你的目标是做一款商业游戏并发布,那当然用引擎更高效;但你的目标是掌握游戏开发的基本原理,或者快速做一个有Python基础的团队能维护的小工具,Pygame反而更合适。
| 方案 | 上手门槛 | 适合场景 | 不建议的场景 |
|---|---|---|---|
| Pygame | 低,懂Python基础即可 | 学习2D原理、小游戏、教育软件 | 大型3D、高性能需求 |
| Arcade | 低,API更现代化 | 类似Pygame的教学与2D项目 | 生态小,资料少 |
| Godot | 中,需学GDScript | 中小型独立游戏、跨平台发布 | 想用纯Python开发 |
| Unity | 高,需学C#/编辑器 | 商业游戏、3D游戏 | 只想快速验证逻辑 |
还有个很现实的原因:Pygame项目可以直接用你熟悉的一整套Python工具链——pip安装、unittest测试、Git版本管理。上次我给一个游戏加了个排行榜功能,直接用Python内置的sqlite3就搞定了,前后不到一小时。这种"跟日常开发工具无缝衔接"的体验,在大型引擎里反而不容易得到。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 从安装到弹出第一个窗口,这些细节能给你省半天时间
2.1 安装的完整姿势与版本选择
安装Pygame几乎是整个项目里最简单的一步,但我在很多新手群里见过有人卡在这里半天。先记住一个原则:永远不要直接敲 pip install pygame,而是用 python -m pip install pygame。区别在于:直接敲pip,系统可能调用的是另一个Python环境的pip(尤其是电脑上装了多个Python版本的场景),结果安装到的位置跟你写代码的环境不是同一个,然后import就报ModuleNotFoundError。
建议在自己项目的虚拟环境里安装:
bash复制python -m venv venv
# Windows:
venv\Scripts\activate
# macOS / Linux:
source venv/bin/activate
python -m pip install pygame
版本选择上,主流的Pygame 2.x系列就够用。另外还有一个社区维护的发行版叫pygame-ce,修复了许多老问题、性能也更好,新项目可以直接考虑它。安装方式同样是 python -m pip install pygame-ce,代码里import的库名依然是pygame。
2.2 验证环境的正确方式
装完之后别急着写代码,先验证环境是否正常。我推荐两个命令:
bash复制python -m pygame --version
python -m pygame.examples.aliens
第一个命令打印版本号,确认安装成功;第二个命令会跑起来一个Pygame自带的"外星人"示例游戏,如果这个能正常弹出窗口、响应操作,说明你的图形环境和SDL驱动都正常。这一步看似多余,实则非常有用——它能帮你把"Pygame没装好"和"你的代码有问题"两类问题区分开。
如果在Linux上运行示例游戏报错,通常是缺少SDL依赖。Debian/Ubuntu系可以用 sudo apt install python3-pygame 或者安装 libsdl2-2.0-0、libsdl2-mixer-2.0-0、libsdl2-image-2.0-0 这几个包。macOS和Windows上一般不会遇到这个问题,官方wheel都是打包好依赖的。
2.3 第一个窗口:理解Surface与事件
验证完环境,我们来写人生第一个Pygame窗口。这是最精简的可用版本:
python复制import pygame
pygame.init()
screen = pygame.display.set_mode((800, 600))
pygame.display.set_caption("我的第一个Pygame窗口")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
pygame.quit()
别看代码短,里面有两个核心概念值得拆开讲。
第一个是 Surface(表面)。pygame.display.set_mode((800, 600)) 返回的screen就是一个Surface,可以把它理解成一张画布。游戏里所有可见的东西——角色、金币、文字——本质上是把这些内容画到一张张小画布上,再通过 blit 操作贴到主画布上,最后用 pygame.display.flip() 一次性展示出来。
第二个是 事件(Event)。pygame.event.get() 从事件队列里取出所有发生的事件,比如鼠标移动、键盘按下、窗口关闭。游戏循环每帧都要调用它,不仅是为了响应输入,更是为了让程序有机会处理系统消息。如果删掉这段循环,窗口就会变成"未响应"状态,这一点后面踩坑章节还会详细讲。
3. 游戏循环:一切2D小戏法的发动机
3.1 三件事的循环:事件、状态、绘制
每个Pygame游戏,无论多复杂,核心都是一个while循环。这个循环里通常做三件事:处理事件、更新游戏状态、绘制画面。
- 处理事件:响应玩家的输入(按键、鼠标、窗口关闭)。
- 更新状态:根据输入和时间,更新物体位置、分数、生命值等数据。
- 绘制画面:把当前状态画到屏幕上。
一个关键认知是:游戏画面并不是"动起来"的,而是"每帧重新画一遍"。今天的电影是24帧每秒,你的游戏通常是60帧每秒。玩家看到角色移动,其实是每帧位置变化了那么几像素,因为刷新够快,人眼就把这串静态画面脑补成了连续动作。理解了这个,你就理解了游戏开发最底层的逻辑。
3.2 帧率控制与delta time
游戏循环如果不加任何约束,会以计算机能跑多快就跑多快。我见过有人写了个空循环,结果CPU占用直接100%,风扇呼呼转。解决办法是创建时钟对象:
python复制clock = pygame.time.Clock()
# ……
clock.tick(60)
clock.tick(60) 的意思是:让这一帧执行完正好经过约1/60秒。如果代码执行得太快,它就主动睡一会儿;如果执行得太慢,它也不会强行追帧。这行代码同时控制了游戏速度和CPU消耗。
进阶一点的做法是引入 delta time(帧间隔时间)。注意一个陷阱:如果你让金币每帧下落5像素,那么60帧下金币每秒下落300像素;但如果某台机器只能跑30帧,每秒只下落150像素——游戏速度就被硬件性能绑架了。处理方法是把移动量乘以时间系数:
python复制dt = clock.tick(60) # 返回上一帧到现在的毫秒数
coin_y += fall_speed * dt / 16.67 # 按60帧基准换算
这样无论帧率是30还是120,金币的每秒位移量都保持一致。对简单小游戏来说,固定帧率下直接写像素值更省事,但一旦你的游戏复杂度上来,或者想在不同性能的机器上跑,delta time是必须过的坎。
3.3 Rect与坐标系统:新手最容易搞反的事
Pygame里的每个可交互对象,核心都是一个 Rect(矩形)。Rect通过 (x, y, width, height) 定义,分别表示左上角坐标、宽和高。一旦有了Rect,很多操作就变得极其优雅:
python复制rect = pygame.Rect(100, 200, 50, 50)
print(rect.center) # (125, 225),自动算中心点
rect.move_ip(10, 0) # 原地向右移动10像素
同时要记住一个反直觉的坐标规则:Pygame的y轴向下为正。左上角是(0, 0),x向右增大,y向下增大。很多初学者第一次画图时,明明写了y=100,结果物体出现在屏幕下方,就是因为把数学坐标系里的y方向搞反了。这个坐标系统是所有2D图形库的通用约定(SDL、HTML Canvas都是如此),习惯它比抱怨它更重要。
Rect还自带了碰撞检测方法,其中最常用的是 colliderect():
python复制if player_rect.colliderect(coin_rect):
score += 1
这就是2D游戏里最主流的碰撞判定方式,简单、高效、够用。后面踩坑章节我会展开说它有哪些边缘情况。
4. 手写一个能跑能玩的"接金币"游戏
4.1 设计拆解:先想清楚再动手指
讲完基础,来做一个完整的游戏。选择"接金币"这个玩法是有讲究的:它只有三件事——玩家移动、金币下落、碰撞判定,但涵盖了事件处理、运动、碰撞、计分、游戏结束状态这几个游戏开发的核心环节,非常适合作为第一个练手项目。
游戏规则设计如下:
- 玩家控制屏幕底部的蓝色平板,用左右方向键(或A/D键)移动。
- 金币从屏幕上方随机横坐标处掉落。
- 接住一个金币,分数+1;漏掉一个金币,生命-1。
- 初始3条命,生命归零则显示"游戏结束",按空格键重新开始。
技术选型上,我故意先用最朴素的方式实现:金币用一个列表存坐标,而不是直接用Pygame的Sprite类。原因很简单——先把逻辑跑通,再谈抽象。你看完代码就明白,这个结构虽然朴素,但完全够用,而且很好懂。
4.2 完整代码
以下代码在Python 3.9+和Pygame 2.x上测试通过,复制保存为 catch_coin.py 即可运行:
python复制import pygame
import random
pygame.init()
SCREEN_WIDTH = 480
SCREEN_HEIGHT = 700
FPS = 60
FALL_SPEED = 5
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BLUE = (70, 140, 255)
GOLD = (255, 200, 40)
RED = (220, 60, 60)
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.display.set_caption("接金币")
clock = pygame.time.Clock()
font = pygame.font.SysFont("SimHei", 32)
# 玩家
player_w, player_h = 90, 20
player_x = SCREEN_WIDTH // 2 - player_w // 2
player_y = SCREEN_HEIGHT - 80
player_speed = 8
# 金币
coins = []
coin_r = 12
coin_interval = 0
score = 0
lives = 3
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE and lives <= 0:
# 游戏结束后按空格重来
lives = 3
score = 0
coins.clear()
if lives > 0:
# 1. 玩家移动
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] or keys[pygame.K_a]:
player_x -= player_speed
if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
player_x += player_speed
player_x = max(0, min(SCREEN_WIDTH - player_w, player_x))
# 2. 生成金币(每30帧生成一个)
coin_interval += 1
if coin_interval >= 30:
coins.append([random.randint(coin_r, SCREEN_WIDTH - coin_r), -coin_r])
coin_interval = 0
# 3. 金币下落与碰撞判定
player_rect = pygame.Rect(player_x, player_y, player_w, player_h)
for coin in coins[:]:
coin[1] += FALL_SPEED
if coin[1] > SCREEN_HEIGHT + coin_r:
coins.remove(coin)
lives -= 1
continue
coin_rect = pygame.Rect(
coin[0] - coin_r, coin[1] - coin_r, coin_r * 2, coin_r * 2
)
if player_rect.colliderect(coin_rect):
coins.remove(coin)
score += 1
# 4. 绘制画面
screen.fill(WHITE)
pygame.draw.rect(screen, BLUE, (player_x, player_y, player_w, player_h))
for coin in coins:
pygame.draw.circle(screen, GOLD, (coin[0], coin[1]), coin_r)
score_img = font.render(f"分数: {score}", True, BLACK)
lives_img = font.render(f"生命: {lives}", True, RED if lives <= 1 else BLACK)
screen.blit(score_img, (15, 15))
screen.blit(lives_img, (SCREEN_WIDTH - 130, 15))
if lives <= 0:
over_img = font.render("游戏结束,按空格重来", True, BLACK)
screen.blit(
over_img,
(SCREEN_WIDTH // 2 - over_img.get_width() // 2, SCREEN_HEIGHT // 2),
)
pygame.display.flip()
clock.tick(FPS)
pygame.quit()
4.3 代码执行要点与体验改进
讲几个这段代码里藏着的小细节。
第一,修改列表时用了 for coin in coins[:]。这是Python里一个经典坑:如果你在遍历列表的过程中直接删除列表元素,会导致跳过元素甚至索引错乱。coins[:] 创建了一份浅拷贝,遍历的是拷贝,修改的是原列表,安全可靠。
第二,碰撞判定用矩形而非圆形。虽然金币画出来是圆形,但碰撞检测用的是它的外接矩形。对于这种小游戏,矩形判定足够准确,而且性能好。如果你追求更精确的圆形碰撞,可以比较两个圆心的距离是否小于半径之和,这在第5章会给出代码。
第三,字体用 pygame.font.SysFont("SimHei", 32)。这里有个隐患:SimHei是Windows系统字体,在macOS和Linux上不一定存在,换成其他操作系统可能会报错或显示方框。可靠的方案是把一个中文字体文件(如'msyh.ttf')放到项目目录里,然后用 pygame.font.Font("msyh.ttf", 32) 加载。后面坑位章节我会专门聊字体问题。
游戏本身能跑了,但体验还可以继续打磨。比如:金币下落速度随分数提高而逐渐加快;偶尔生成一个红色炸弹,接到会扣一条命;加一个背景音乐和接到金币时的音效。这些改动都不复杂,强烈建议你拿到代码后先跑通,再一个一个加上去,体会游戏手感是怎么调出来的。
5. 实战中踩过的坑:从黑屏到音效不出的完整排错记录
5.1 黑屏、窗口无响应:事件循环没被喂饱
新手最常见的两个问题,症状高度相似,都是"窗口弹出来但不正常"。第一种是全黑窗口,第二种是窗口显示出来了但拖动几下就提示"未响应"。前者的原因通常是忘了写 pygame.display.flip()。你的所有绘制操作都发生在内存画布上,不调用flip,画面永远不会更新到屏幕上。
后者的原因则是事件循环没被及时处理。操作系统会定期给程序发送消息,如果程序长时间不调用 pygame.event.get(),系统就认为程序卡死了。这个问题的隐蔽之处在于:如果游戏逻辑里有个耗时操作(比如加载大图片、sleep过久),窗口就会在这段时间里"假死"。解决办法是把耗时操作放到主循环之外做,或者用异步方式加载。
排查这类问题,我的固定套路是:先跑官方示例游戏,确认环境没问题;再最小化自己的代码,逐段二分注释,看是哪一段引入的问题。时间长了你会发现,90%的"窗口异常"都能归到这两个原因里。
5.2 中文乱码与字体加载
Pygame的默认字体是不支持中文的。如果你直接 pygame.font.Font(None, 32) 然后渲染中文,屏幕上大概率是一堆方框。这是个很劝退的问题,因为小游戏放个"分数"、"生命"再正常不过了。
最省事的方案是 SysFont("SimHei", 32),Windows上基本能用。但放到Linux服务器上跑就糟了。我踩过这个坑:游戏在自己电脑上跑得好好的,部署到一台Linux机器上一运行,中文全变方框。排查半天发现Linux根本没有SimHei字体。
可靠的解决方案:把字体文件作为游戏资源一起分发。中文字体文件一般比较大(几MB到十几MB),但为了显示正常可以接受。代码这样写:
python复制import os
import pygame
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
font_path = os.path.join(BASE_DIR, "fonts", "msyh.ttf")
font = pygame.font.Font(font_path, 32)
还有个性能细节:不要在游戏循环里反复调用 font.render()。每次渲染文字都会生成一个新的Surface,如果每帧都渲染"分数:xxx"这种动态文本,会白白增加很多开销。更好的做法是只在分数变化时重新渲染,或者预渲染所有可能的文本。
5.3 碰撞判定"怪怪的":Rect与坐标的常识陷阱
用 colliderect() 做碰撞判定,大部分时候没问题,但有两个边界情况必须清楚。
第一个是 高速物体穿模(tunneling)。如果金币每帧移动很多像素,它可能在这一帧位于玩家上方、下一帧就落到玩家下方,两帧之间完全没有重叠,碰撞检测就漏过了。这在高速弹球、子弹类游戏中尤其明显。解决办法是:限制单帧最大位移量,或者用"扫掠检测"(把运动的路径当做一个细长的矩形来检测)。
第二个是 视觉与判定框不一致。圆形金币用外接矩形判定,四个角其实是空白的,会出现"明明没碰到金币却加了分"的违和感。要精确的话,用圆心距离判定:
python复制import math
dx = coin_x - player_center_x
dy = coin_y - player_center_y
if math.hypot(dx, dy) < coin_r + player_r:
score += 1
这个方案比矩形稍微多一点计算量,但对数量不多的小游戏毫无压力。实用主义的选择是:如果玩家不仔细较真,矩形判定就够;如果手感上总有人说"不对",再升级到圆形判定。
5.4 资源文件路径:打包后找不到图片
游戏里要加载图片、字体、音效,最常见的问题是相对路径失效。原因在于:代码运行时的工作目录(CWD)不一定是代码所在目录。如果你从别的目录启动程序,"images/coin.png" 这个相对路径就找不到文件了。
解决办法是始终基于代码文件位置来拼接路径:
python复制import os
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
img_path = os.path.join(BASE_DIR, "images", "coin.png")
这个问题在打包发布时更是炸雷。用PyInstaller把游戏打包成exe后,资源文件不会自动跟着进安装包,必须在打包时显式指定:
bash复制pyinstaller --onefile --windowed \
--add-data "images;images" \
--add-data "fonts;fonts" \
catch_coin.py
Windows上用分号分隔源路径和目标路径,macOS/Linux上用冒号。打包后程序运行时,资源会被解压到临时目录,所以光靠 __file__ 还不够,要兼容PyInstaller的运行环境。这个属于进阶问题,但提前心里有数,能省不少崩溃后的排查时间。
6. 让游戏更像游戏:优化、资源与后续方向
6.1 用Sprite/Group重构之前的接金币
接金币游戏跑通之后,你会发现代码里"画金币"和"金币移动"的逻辑散落在主循环里。当游戏里的对象越来越多,这种写法会迅速失控。Pygame提供了Sprite和Group机制来解决这个问题。
Sprite就是"精灵",一个自带图像和位置的游戏对象;Group是精灵的分组,可以批量操组内所有精灵。重构之后,金币的逻辑可以整齐地封装起来:
python复制class Coin(pygame.sprite.Sprite):
def __init__(self, x, y):
super().__init__()
self.image = pygame.Surface((coin_r * 2, coin_r * 2), pygame.SRCALPHA)
pygame.draw.circle(self.image, GOLD, (coin_r, coin_r), coin_r)
self.rect = self.image.get_rect(center=(x, y))
def update(self):
self.rect.y += FALL_SPEED
if self.rect.top > SCREEN_HEIGHT:
self.kill() # 自动从所有Group中移除
主循环里只需三行:
python复制coins_group.add(Coin(random.randint(...), -coin_r))
coins_group.update()
pygame.sprite.groupcollide(coins_group, player_group, True, False)
groupcollide 一行就完成了碰撞检测、删除金币、返回碰撞列表,代码量立刻瘦身一大截。而且 sprite.Group 内部已经优化过批量绘制和碰撞检测,性能比手写列表遍历更好。我的建议是:第一个版本用朴素写法理解原理,第二个版本立刻用Sprite重构,这个过程本身就是最好的学习。
6.2 性能优化:从渲染到碰撞
Pygame项目常见的性能瓶颈,按出现频率排序大概是:每帧创建新对象、每帧渲染文字、没必要的Surface复制、碰撞检测写了多重循环。
几个立竿见影的优化策略:
- 图片加载后立即转换格式。
pygame.image.load(path).convert()把图片转成与屏幕一致的像素格式,convert_alpha()则保留透明通道。不转换虽然也能显示,但blit时会有额外的像素格式转换开销,图片多时差距明显。 - 避免在循环内创建对象。尤其是Surface和Rect,能用局部变量复用的就复用。
- 文字预渲染。静态文字(标题、按钮)在循环外渲染一次存起来;动态文字只在数值变化时重新渲染。
- 碰撞检测缩小范围。多个物体之间测碰撞时,先用简单的矩形快速排除明显不相交的物体,再对候选集做精确检测。
对多数小游戏来说,做好这四点就绰绰有余了。真要追求更高的性能,可以考虑用 pygame.sprite 的LayeredUpdates做分层渲染、用 pygame.mask 做像素级碰撞,但这些都是后话,先把游戏做完比什么都重要。
6.3 音效、图片与发布打包
游戏没有声音,就像电影没有说话,总觉得少了灵魂。Pygame的音效模块是 pygame.mixer,用起来很直接:
python复制pygame.mixer.init()
coin_sound = pygame.mixer.Sound("sounds/coin.wav")
coin_sound.play()
注意几个点:mixer.init() 要在 pygame.init() 之后调用(实际上pygame.init()默认会连带初始化mixer,但显式调用更稳妥);支持wav和ogg格式,mp3格式兼容性稍差;背景音乐用 pygame.mixer.music.load() 加 pygame.mixer.music.play(-1),参数-1表示循环播放。
图片素材方面,我常用的是 Kenney.nl 和 OpenGameArt,上面有大量免费可商用的游戏素材包,包括角色、道具、背景和音效。不用自己做美术,也能把游戏做得有模有样。我自己第一版接金币用的是纯色矩形,加上图片素材之后,观感立刻提升了两个档次。
发布给朋友试玩,用PyInstaller打包是最主流的方式。打包流程上面提过,补充两点经验:一是加上 --windowed 参数避免运行时弹出黑色控制台窗口;二是打包后一定要在干净环境(没有装Python的机器)上测试一次,因为很多依赖缺失问题在开发机上根本暴露不出来。
6.4 进阶资源推荐与学习路径
如果你把接金币这个游戏做完并改出了自己想要的玩法,恭喜你,Pygame的入门阶段就算功德圆满了。接下来按这个顺序进阶,成长曲线会很平滑:
- 经典复刻:做一个贪吃蛇,重点练二维网格和方向控制;做一个打砖块,重点练反弹物理和碰撞响应;做一个超级玛丽式横版过关,重点练重力、跳跃和Tile地图。
- 状态管理:给游戏加上"开始界面-游戏进行-暂停-游戏结束"的状态机。这是我强烈推荐做的一步,它会让你的代码结构脱胎换骨。
- 工具链:学习用
pygame_gui或pygame_menu做UI,用tmx库加载Tiled地图编辑器生成的地图。 - 官方资料:Pygame的官方文档(pygame.org/docs)里每个模块都有示例代码,
pygame.examples包里还有各种小Demo,是极好的学习素材;平时多翻翻源码也是提升Python水平的一条捷径。
最后说点心里话。我见过太多人下载了Pygame、跑通了官方示例,然后就没有然后了。游戏开发这门手艺,卡在"看懂了"和"写出来"之间的鸿沟,唯一的渡船就是亲手写完一个完整的项目。哪怕丑陋、哪怕粗糙,只要你能把"玩家动起来、金币掉下来、撞到了有反应"这三件事连成闭环,你就已经真正跨进了游戏开发的大门。
