拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Python项目实战:用pygame从零开发贪吃蛇小游戏

Python项目实战:用pygame从零开发贪吃蛇小游戏

很多 Python 初学者会卡在同一个位置:语法书看完了,练习题也做了一些,但一说到“写一个自己的程序”,就不知道从哪下手。变量、列表、循环、条件判断,每个知识点单独看都懂,组合起来却拼不成一个完整的东西。这种“什么都学了,但什么都不会做”的挫败感,往往会劝退很多人。

贪吃蛇恰好是一剂对症的药。它代码量不大,逻辑却完整:有主循环、有事件监听、有状态更新、有碰撞检测,还能通过操作键盘实时看到效果。学会它,你不仅能跑通一个“能玩”的游戏,还能真正理解游戏乃至很多图形化程序背后的运行机制:不断刷新画面、处理用户输入、根据规则更新状态。这篇文章会从零开始,带你把贪吃蛇的每一个环节拆开讲清楚,并给出可以直接运行的 Python 源码。读完你会发现,从“看懂语法”到“做出程序”,中间只差一个完整项目的距离。

1. 为什么贪吃蛇是 Python 练手的好项目

1.1 新手最需要突破的那个坎

初学者最大的问题,不是语法学得不够多,而是缺少把一个需求拆成可执行步骤的经验。看教程时跟着敲代码感觉都能懂,一旦自己要写,思维就断了:先写什么?数据怎么存?循环放在哪里?什么时候结束?

想要跨越这个阶段,最好的办法就是找一个“麻雀虽小,五脏俱全”的项目去完整实现一遍。贪吃蛇正好满足这个条件。它不像扫雷、俄罗斯方块那样有复杂的界面布局和交互逻辑,也不像管理系统那样要拼接大量 CRUD 代码。一个标准贪吃蛇,核心代码一百行左右就能跑起来,但其中涉及的编程知识点非常集中。

更重要的是,做完它之后你能获得即时反馈。程序不是打印一段文字就结束,而是生成了一个真正可以玩的窗口游戏。这种成就感,对保持学习动力非常关键。

1.2 贪吃蛇覆盖了多少核心知识点

我们不妨清点一下,一个最小可玩的贪吃蛇会用到哪些 Python 基础:

知识点在贪吃蛇中的角色
变量与数据类型记录窗口宽高、格子大小、蛇的位置、分数
列表(List)存放蛇身体的每一节坐标
字典或元组表示移动方向、RGB 颜色
条件判断判断键盘按键、撞墙、撞自己、是否吃到食物
while 循环驱动整个游戏主循环,不断刷新
for 循环遍历事件队列,以及绘制蛇的每一节身体
函数把“画蛇”“生成食物”“主循环”拆成独立逻辑
模块导入导入 pygame、random、sys 等标准模块
异常处理处理安装或运行中的常见异常

如果说基础语法是一颗颗散落的珠子,贪吃蛇就是那根把它们穿起来的线。做完这个项目,你对这些语法的理解会从“认识”变成“会用”。

1.3 这篇教程能帮你学到什么

读完这篇文章并亲手敲完代码,你会得到三样东西。

第一,一个可以运行、可以和朋友分享的贪吃蛇小游戏。第二,一套通用的游戏开发思维:初始化 → 事件处理 → 状态更新 → 碰撞检测 → 画面绘制,这套循环几乎适用于所有 2D 小游戏。第三,遇到问题时排查错误的思路,比如安装失败怎么处理、方向不受控是什么原因、画面卡顿怎么优化。

如果你是一个只学了 Python 基础、还没做过完整项目的新手,这篇教程值得你从头到尾认真走一遍。

2. 游戏设计思路与核心概念

2.1 贪吃蛇的基本游戏规则

在写代码之前,先把规则理清楚。贪吃蛇的玩法并不复杂:一条蛇在矩形区域内移动,吃到一个食物后身体变长一节,同时分数增加;如果蛇头撞到墙壁,或者撞到自己的身体,游戏结束。

这三条规则中,真正需要仔细设计的是蛇的移动逻辑。蛇在移动时,并不是凭空瞬移,而是“头部向前进一格,身体跟着前一节走”。如果用列表来存储蛇身,每一节就是一个[x, y]坐标,那么移动就非常直观:把新头部插入列表最前面,如果吃到食物就不删除尾部,身体变长;没吃到食物就删除尾部,身体长度保持不变。

理解这一点之后,整个游戏的数据模型就清晰了。蛇是“一个坐标列表”,食物是“一个坐标”,方向是“一个向量”,分数是“一个整数”。

2.2 pygame 在游戏里扮演什么角色

Python 标准库里没有内置图形化游戏开发模块,所以需要引入 pygame。pygame 是一个跨平台的 Python 游戏开发库,封装了窗口创建、图像绘制、事件处理、音效播放等底层能力。你可以把它理解成一个“游戏基础设施供应商”:你不需要自己去操作操作系统窗口和显卡,只需要调用 pygame 提供的接口,就能在屏幕上画方块、监听键盘、刷新画面。

python 开发 2D 小游戏通常有几种选择:tkinter 是标准库自带的 GUI 库,也能做简单游戏,但性能一般;turtle 更适合教学演示,做游戏有些吃力;pygame 则是最均衡的选择,资料多、功能全、社区活跃,遇到问题基本都能搜到答案。贪吃蛇这类格子游戏,用 pygame 做是最顺手的方式。

2.3 窗口、坐标与格子模型

pygame 的窗口坐标系以左上角为原点,x 轴向右增加,y 轴向下增加。在贪吃蛇里,屏幕并不是逐个像素操作的,而是被划分成了等间距的格子。每个格子的大小决定蛇的“粗细”和移动的最小单位。

举个例子,如果窗口宽度是 800,格子大小是 20,那么水平方向就有 40 个格子。蛇头每次只移动一个格子的距离。这样做有两个好处:一是移动逻辑简单,不需要处理半格位置;二是绘制方便,每个身体节就是一个正方形方块。食物生成时,它的坐标也必须落在格子交点上,否则就会和蛇错开。

设计代码时,常量CELL_SIZE贯穿始终。窗口宽高、蛇的初始位置、食物坐标、墙壁边界,全部由它派生出来,这样后续改窗口大小只需要改一个常量。

3. 环境准备与 pygame 安装

3.1 Python 环境要求

运行这个贪吃蛇程序,需要本机已经装好 Python 和 pip。建议使用 Python 3.8 及以上版本,pygame 选择 2.x 版本。版本的具体差异不影响本文的核心逻辑,但新版本在兼容性上更稳妥。

可以用下面的命令确认 Python 环境是否就绪:

python --version pip --version

如果两条命令都能正常输出版本号,说明基础环境没有问题。Windows 用户如果使用python命令报错,可以试试py或python3,这取决于你安装 Python 时的环境变量配置。

3.2 安装 pygame

在命令行中执行:

pip install pygame

如果你的机器同时存在多个 Python 版本,建议使用 python -m pip 这种更明确的安装方式,避免装到了错误的环境:

python -m pip install pygame

在部分网络环境下,直接从官方源下载可能很慢。遇到超时或连接失败,可以切换到国内镜像源,例如清华 PyPI 镜像:

pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple

这条命令把包的下载源临时指定到了国内镜像节点,下载速度通常会快很多。

3.3 验证安装是否成功

安装完成后,在命令行运行:

python -c "import pygame; print(pygame.ver)"

能打印出版本号(例如2.5.2这种格式),就说明 pygame 已经可以正常导入了。网上很多教程会让新手直接复制一段包含pygame.display.set_mode的代码来测试,如果窗口正常弹出再关闭,就说明安装没有问题。

另外还有一个小建议:如果你在多个项目之间切换,或者不确定全局环境是否被污染,可以考虑用虚拟环境(venv 或 conda)隔离依赖。这样即使某个项目的依赖装坏了,也不会影响其他项目。

4. 贪吃蛇核心逻辑拆解

4.1 游戏的初始化部分

初始化要完成四件事:导入模块、创建窗口、定义常量、设置蛇的初始状态。

窗口尺寸建议设置成格子大小的整数倍。例如SCREEN_WIDTH = 800、SCREEN_HEIGHT = 600、CELL_SIZE = 20,那么水平方向就是 40 个格子,垂直方向就是 30 个格子。蛇的初始位置应该放在屏幕中间偏左的位置,并且让蛇身至少有两三节,这样开局时蛇的形态才看得清楚。

pygame 的初始化流程是这样的:

pygame.init() screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("贪吃蛇 - Python 入门小游戏")

pygame.init()负责初始化所有 pygame 模块,set_mode创建窗口,set_caption设置窗口标题。如果忘了调用init,很多功能会直接报错或者表现异常。

蛇的初始状态需要重点设计。如果用多个变量分别存储头、第二节、第三节,代码会越来越难维护。正确做法是用一个列表,列表里的每个元素都是一个[x, y]坐标:

snake = [[400, 300], [380, 300], [360, 300]]

初始方向可以定为向右移动,对应向量RIGHT = (1, 0)。注意方向判断用的并不是字符串,而是坐标差向量,这样方便直接计算新头部位置。

4.2 主循环与事件处理

游戏运行的核心是一个 while 循环,只要游戏没有结束,循环就不断重复。每次循环做四件事:处理事件、更新状态、绘制画面、控制节奏。

事件处理是指程序询问“用户刚才做了什么操作”。pygame 会把鼠标移动、点击、键盘按下、窗口关闭等动作封装成事件,存放在事件队列中。程序通过遍历事件队列来获取:

for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP and direction != DOWN: direction = UP elif event.key == pygame.K_DOWN and direction != UP: direction = DOWN elif event.key == pygame.K_LEFT and direction != RIGHT: direction = LEFT elif event.key == pygame.K_RIGHT and direction != LEFT: direction = RIGHT

这段代码里有一个非常关键的细节:方向反转限制。如果当前正在向右移动,用户突然按左键,蛇头会直接往反方向走,穿过整个身体,这与真实贪吃蛇规则不符。因此每个方向按键都配了一个“方向不为反向”的判断,比如向右时不允许直接向左,向上时不允许直接向下。

理解事件循环的关键,在于它解决了“外部动作什么时候影响游戏状态”的问题。事件处理本身不绘制画面、不计算移动,它做的只是修改direction这个全局变量,真正让方向生效的是下一步的状态更新。

4.3 蛇的移动与方向控制

蛇的移动是整个游戏最核心的代码。每次刷新,蛇头应该朝当前方向前进一格,身体则按“追尾”的方式向前收进。

先根据方向向量计算出新蛇头的位置:

new_head = [ snake[0][0] + direction[0] * CELL_SIZE, snake[0][1] + direction[1] * CELL_SIZE ]

然后决定是否要从尾部删除一节:

snake.insert(0, new_head) if new_head == food: score += 10 food = generate_food() else: snake.pop()

insert(0, new_head)把新头部加到列表最前面。如果吃到了食物,尾部不删除,蛇整体变长一节;没有吃到食物,则用pop()删除最后一节,保持身体长度不变。

这个设计非常优雅,因为它不需要逐个更新蛇的每一节位置,只需要处理头部和尾部两端的数据。很多新手想复杂的“让每一节跟着前一节走”,结果写了一堆循环还容易出错。实际上,列表的两端操作天然就实现了蛇的移动方式。

方向控制要再次强调“禁止反向”的判断条件。方向变化会立即生效,如果允许反向,蛇头可能在一帧内撞到紧跟在身后的身体第二节,从而出现“自己撞死自己”的诡异现象。

4.4 食物生成逻辑

食物生成看似简单,但有一个容易忽略的条件:食物不能生成在蛇身上。如果食物恰好落在蛇的某个身体节上,就会出现“吃不到食物”和“食物一直存在”的冲突。

解决方式是用一个 while 循环不断随机生成坐标,直到坐标不与蛇重叠为止:

def generate_food(): while True: x = random.randint(0, (SCREEN_WIDTH - CELL_SIZE) // CELL_SIZE) * CELL_SIZE y = random.randint(0, (SCREEN_HEIGHT - CELL_SIZE) // CELL_SIZE) * CELL_SIZE if [x, y] not in snake: return [x, y]

这里的关键是对齐格子:先计算一共有多少个格子,在“格子编号”中取随机数,再乘回格子大小,保证食物永远落在格子交点上。例如random.randint(0, 39) * 20,结果只可能是 0、20、40,一直到 780,绝对不会出现 35 这样的坐标。

4.5 碰撞检测与游戏结束

碰撞检测决定游戏何时结束,这也是很多新手容易漏掉的一环。

第一种碰撞是撞墙。蛇头坐标超出窗口边界,游戏结束:

if (new_head[0] < 0 or new_head[0] >= SCREEN_WIDTH or new_head[1] < 0 or new_head[1] >= SCREEN_HEIGHT): running = False continue

第二种碰撞是撞自己。新头部如果已经存在于蛇身列表中,说明蛇头即将撞上身体,游戏结束:

if new_head in snake: running = False continue

需要特别注意的是,这两段检测必须发生在snake.insert(0, new_head)之前。如果在插入之后再判断,新头部已经进入了列表,那么new_head in snake永远为真,蛇会在移动的第一步就把自己撞死。这个错误在实际编写中非常容易出现,最好先把新头部算出来,做完全部碰撞检测,再决定是插入还是弹掉尾部。

游戏结束后,主循环退出,然后显示一句提示并退出 pygame:

pygame.quit() sys.exit()

4.6 分数显示与画面刷新

每次循环结尾,需要把最新的画面绘制到屏幕上。pygame 的绘制分三步:先填充背景色清空上一帧画面,再绘制所有对象,最后调用pygame.display.flip()刷新屏幕。

screen.fill(BLACK) draw_snake() pygame.draw.rect(screen, RED, pygame.Rect(food[0], food[1], CELL_SIZE, CELL_SIZE)) font = pygame.font.Font(None, 36) text = font.render(f"Score: {score}", True, WHITE) screen.blit(text, (10, 10)) pygame.display.flip() clock.tick(10)

在画面上绘制文字需要用到字体对象。pygame.font.Font(None, 36)表示使用默认字体,大小为 36。分数用英文单词 “Score” 显示,是为了避免默认字体不支持中文导致出现方框乱码。

最后一行clock.tick(10)控制帧率,也就是每秒最多循环 10 次。数值越大,蛇移动越快,游戏难度越高。对于新手项目来说,10 是比较舒服的起步速度。

5. 完成代码实现与运行验证

5.1 完整源码

把前面所有逻辑整合到一起,就是一个可以直接运行的贪吃蛇游戏。建议新建一个文件,命名为snake.py,复制下面的代码。

import pygame import random import sys # ---------- 初始化 ---------- pygame.init() # ---------- 常量 ---------- SCREEN_WIDTH = 800 # 窗口宽度 SCREEN_HEIGHT = 600 # 窗口高度 CELL_SIZE = 20 # 蛇一格的长度 # RGB 颜色 BLACK = (0, 0, 0) WHITE = (255, 255, 255) GREEN = (0, 200, 0) RED = (255, 0, 0) # 移动方向(单位向量) UP = (0, -1) DOWN = (0, 1) LEFT = (-1, 0) RIGHT = (1, 0) # ---------- 创建窗口 ---------- screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("贪吃蛇 - Python 入门小游戏") clock = pygame.time.Clock() # ---------- 蛇的初始状态 ---------- # 每个元素是 [x, y],x 是水平坐标,y 是垂直坐标 snake = [[400, 300], [380, 300], [360, 300]] direction = RIGHT # ---------- 分数 ---------- score = 0 # ---------- 工具函数 ---------- def draw_snake(): """绘制蛇""" for segment in snake: pygame.draw.rect(screen, GREEN, pygame.Rect(segment[0], segment[1], CELL_SIZE, CELL_SIZE)) def generate_food(): """随机生成食物,确保不落在蛇身上""" while True: x = random.randint(0, (SCREEN_WIDTH - CELL_SIZE) // CELL_SIZE) * CELL_SIZE y = random.randint(0, (SCREEN_HEIGHT - CELL_SIZE) // CELL_SIZE) * CELL_SIZE if [x, y] not in snake: return [x, y] # ---------- 主循环 ---------- def main(): global direction, score food = generate_food() running = True while running: # 处理事件 for event in pygame.event.get(): if event.type == pygame.QUIT: running = False elif event.type == pygame.KEYDOWN: if event.key == pygame.K_UP and direction != DOWN: direction = UP elif event.key == pygame.K_DOWN and direction != UP: direction = DOWN elif event.key == pygame.K_LEFT and direction != RIGHT: direction = LEFT elif event.key == pygame.K_RIGHT and direction != LEFT: direction = RIGHT # 计算新蛇头 new_head = [snake[0][0] + direction[0] * CELL_SIZE, snake[0][1] + direction[1] * CELL_SIZE] # 撞墙检测 if (new_head[0] < 0 or new_head[0] >= SCREEN_WIDTH or new_head[1] < 0 or new_head[1] >= SCREEN_HEIGHT): running = False continue # 撞自己检测 if new_head in snake: running = False continue # 移动 snake.insert(0, new_head) if new_head == food: score += 10 food = generate_food() else: snake.pop() # 绘制画面 screen.fill(BLACK) draw_snake() pygame.draw.rect(screen, RED, pygame.Rect(food[0], food[1], CELL_SIZE, CELL_SIZE)) font = pygame.font.Font(None, 36) text = font.render(f"Score: {score}", True, WHITE) screen.blit(text, (10, 10)) pygame.display.flip() clock.tick(10) # 游戏结束画面 screen.fill(BLACK) font = pygame.font.Font(None, 60) over_text = font.render("Game Over", True, WHITE) score_text = font.render(f"Final Score: {score}", True, WHITE) screen.blit(over_text, (SCREEN_WIDTH // 2 - 130, SCREEN_HEIGHT // 2 - 40)) screen.blit(score_text, (SCREEN_WIDTH // 2 - 160, SCREEN_HEIGHT // 2 + 20)) pygame.display.flip() pygame.time.wait(2000) pygame.quit() sys.exit() if __name__ == "__main__": main()

5.2 如何运行

在命令行进入snake.py所在目录,然后执行:

python snake.py

如果你的环境里 Python 3 需要使用python3命令,则执行:

python3 snake.py

运行后应该出现一个黑色窗口,窗口标题是“贪吃蛇 - Python 入门小游戏”。窗口中央有三节绿色的蛇体,窗口中某个位置有一个红色食物,左上角显示白色分数。用键盘上的上下左右方向键控制蛇的移动即可开始游戏。

5.3 怎么判断游戏运行正常

判断程序是否正常的标准很简单:蛇能响应方向键移动;吃到红色食物后,分数从 0 变成 10,蛇身明显变长;蛇头撞墙或撞到自己时,画面出现 “Game Over” 和最终分数,2 秒后程序退出。

如果操作方向键没有反应,先确认窗口是否处于激活状态,也就是鼠标是否点过游戏窗口。如果蛇完全不显示,检查draw_snake函数是否被正确调用,常见错误是把这段绘制代码写到了主循环外部。如果是窗口一闪而过,多半是主循环没有进入,可以检查缩进是否对齐。

6. 常见问题与排查思路

问题现象可能原因排查方式解决方案
pip install pygame 超时或报错网络访问官方源不稳定查看 pip 错误信息尾部使用清华镜像源pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple
import pygame 提示 ModuleNotFoundError安装到的 Python 环境与运行命令不一致执行pip show pygame,确认安装路径使用python -m pip install pygame统一解释器
窗口打开后立刻闪退主循环没有执行或代码缩进错误在终端运行,观察报错的 traceback检查 while 循环是否被缩进进main()内部
游戏画面一动不动事件循环阻塞,或代码只执行了一次在 while 循环内打印调试信息检查循环是否真正进入,方向键分支是否拼写正确
按上键蛇却向下移动方向键处理逻辑写反打印当前方向变量确认 K_UP 对应 UP,且方向判断没有反向
按反方向时蛇立刻死亡缺少反向限制判断检查事件分支中的!= LEFT条件补全四个方向的反向屏蔽判断
蛇消失或食物位置异常食物坐标没有对齐格子打印 food 坐标检查使用random.randint按格子数量生成并乘回CELL_SIZE
显示分数出现中文乱码默认字体不支持中文字符观察终端或窗口显示改用英文Score文本,或指定系统中文字体文件
蛇移动速度过快或过慢clock.tick()数值不合适调整帧率看效果从clock.tick(10)开始微调
吃掉食物后蛇没有变长吃到食物时仍然执行了pop()检查new_head == food分支吃到食物时不删除尾部,只在未吃到时snake.pop()

7. 进阶方向与扩展思路

7.1 难度分级

贪吃蛇完全可以做得更像一个正式游戏。最简单的改进是让速度随分数提升:分数越高,clock.tick的数值越大,蛇移动越快,挑战性也随之增强。

实现时可以把帧率计算放到每次循环里,例如设定speed = 10 + score // 100,这里score // 100表示每吃 10 个食物速度提升 1 档,然后让clock.tick(speed)生效。如果想做得更精细,可以用全局变量维护难度等级,在吃到食物时根据一定条件递增。

7.2 暂停与重新开始

很多游戏都需要暂停和重开功能。暂停只需要在事件处理中增加一个空格键或 P 键判断,用一个布尔变量记录暂停状态。当暂停为 True 时,跳过状态更新和绘制的移动逻辑,只保留事件处理和画面显示。

重新开始则复杂一些,需要重置蛇的初始位置、方向和分数。建议把“初始化蛇的状态”封装成一个函数,比如reset_game(),这样在重开时只需要调用一次,代码不会重复。

7.3 穿墙模式

经典模式里撞墙即死,穿墙模式则允许蛇从一边边界出去,从另一边穿回来。实现方法看起来复杂,实际就是取模运算:

new_head[0] = new_head[0] % SCREEN_WIDTH new_head[1] = new_head[1] % SCREEN_HEIGHT

需要注意,取模之后要保证坐标不会出现负数,pygame 的坐标必须是非负数。这里有两个细节值得注意:一是穿墙模式下“撞墙检测”要改写成判断是否越界并修正坐标;二是不同模式可以做成一个全局设置,方便切换。

7.4 中文显示与游戏音效

默认字体显示不了中文,可以通过pygame.font.Font指定系统内的中文字体文件。Windows 上可以选择类似“微软雅黑”的字体,路径通常在C:/Windows/Fonts/msyh.ttc。Linux 和 macOS 的字体路径则不同,需要根据实际系统调整。

音效部分,pygame 支持读取 wav 等格式的声音文件。最简单的做法是初始化时引入一个“吃到食物就播放一次短音效”的模块,如果不想处理音频文件,也可以跳过这一部分,使用 pygame 提供的简单蜂鸣替代。

7.5 面向对象重构

当游戏功能逐渐增多,全局变量和散落的函数会越来越难管理。进阶的下一步是用类重构。常见的拆法是把游戏拆成Snake类和Game类:Snake负责维护蛇身坐标、移动、转向判断;Game负责主循环、事件处理、生成食物、计分和渲染。

面向对象重构不会改变游戏功能,但会大幅提升代码的可读性和可维护性。对于从入门走向中级阶段的开发者来说,这是非常值得练习的一步。同样的游戏逻辑,先写函数版,再改写为类版,对比两版代码的差异,你对“面向对象到底解决了什么问题”的理解会深刻很多。

7.6 排行榜与数据持久化

给游戏加一个排行榜,记录历史最高分,本质上是在练“文件读写”能力。最简单的方法是用文本文件直接存一个数字:游戏结束时读取文件中的历史最高分,如果当前分数更高,就写回文件。更进一步可以用 JSON 记录多条分数和日期,或者写入数据库中实现真正的“排行榜”。这个扩展对新手也很合适,因为它非常贴近实际工程中“数据保存”的概念。

8. 最佳实践与工程建议

8.1 常量管理,避免魔法数字

贪吃蛇代码里有一类非常容易失控的数字:窗口大小、格子大小、蛇初始坐标、游戏结束文字位置。新手刚开始练习时,会习惯直接把 20、800、600 这些数字散落在代码各处。问题是,一旦想调整格子大小,就必须找到每一个使用到它的地方,很容易漏改。

正确做法是在文件顶部集中定义常量,比如SCREEN_WIDTH、SCREEN_HEIGHT、CELL_SIZE,并且让蛇的初始位置、食物坐标都从常量推导。这也是一种良好的工程习惯,不只是写小游戏需要。

8.2 函数单一职责

把“绘制蛇”“生成食物”“处理事件”“更新移动”拆成独立函数,而不是全部堆在主循环里,收益是明显的。每个函数只做一件事,定位问题时可以按函数去排查。比如发现蛇显示有问题,只需检查draw_snake的绘制逻辑;发现食物生成异常,只需检查generate_food的随机范围。

初次练习时不必过度设计,函数粒度适中即可。如果一个函数超过一屏,或内部逻辑可以明确拆成两件事,就值得继续拆分。

8.3 增量开发,一次验证一个功能

很多新手写游戏时会在编辑器里一口气写完上百行代码,然后运行,却发现一堆错误,最后连错在哪都看不清。更推荐的开发方式是增量式的:先把窗口创建和主循环跑起来,再让一个方块能移动,然后加上食物和碰撞检测,最后加上分数和结束画面。每加一个功能就运行一次,确保当前步没有问题。这样做定位错误的范围会小很多,也更容易建立“代码是可控的”信心。

8.4 注释与文档习惯

给代码写适量注释,也是从新手走向进阶的重要习惯。注释不是把变量名翻译成中文,而是要写出“为什么这样做”。比如“insert(0, new_head) 是把新头部加入列表首部,吃食物时尾部不删除,所以蛇会变长”这类注释,对事后回顾和他人阅读都有很大帮助。

8.5 版本控制与虚拟环境

即使只是个人练手,把项目纳入 git 管理也是很好的实践。从最开始的初版开始提交,每完成一个小功能就提交一次,你就能看到自己的代码如何一步步演变成完整游戏。虚拟环境(venv)则能避免不同项目之间的依赖冲突。一个游戏项目通常只需要一个requirements.txt文件来描述依赖,进入项目目录后用pip install -r requirements.txt即可恢复环境。

9. 总结与下一步建议

这篇文章从“新手学完语法却写不出程序”这个痛点出发,完整实现了一个基于 pygame 的贪吃蛇小游戏。核心知识包括:pygame 的初始化与主循环、事件处理与键盘控制、蛇身的列表存储与移动邏辑、食物的随机生成、撞墙和撞自身检测、分数与结束画面。所有代码合成一个文件即可运行,适合新手反复修改和实验。

下一步建议你亲手把代码运行起来,然后做三次改动:一是把初始速度调快一档,观察手感差异;二是给游戏加暂停功能;三是把“撞墙即死”改成“穿墙模式”。每一次改动都是对既有逻辑的一次理解加深。当你完成了这三项改动,并且能向别人讲清楚“为什么insert(0, new_head)之后不删尾部就能变长”时,你对 Python 和简单游戏开发的理解就已经上了一个台阶。

如果你是一路读到这里的初学者,把这一刻当成起点:游戏已经能跑,剩下的细节就交给你的兴趣和耐心。

返回列表