☰
Python自学练手:用pygame从零实现贪吃蛇小游戏
2026/10/3 11:10:26 网站建设 项目流程

从决定自学 Python 到写出第一款能正常运行、能得分、能失败重来的小游戏,这段过程没有想象中那么难,但也没有“刷完教程就能直接上手”那么轻松。很多自学者应该都有类似的感受:语法知识点看了不少,列表、字典、循环、函数都认识,可真到打开编辑器准备写点东西的时候,脑子里一片空白。我自己的解决办法很直接——不再继续往下看视频,而是找一个稍微有点意思的小项目,边写边查资料。这篇就完整记录我用 Python 做贪吃蛇小游戏的全过程,包括环境搭建、游戏设计思路、完整可运行的代码和一批调试经验,希望给同样处于 Python 入门阶段的朋友做一个参考。

文章内容主要围绕几个问题展开:自学 Python 该做什么练手项目?pygame 怎么安装和使用?贪吃蛇这类小游戏的核心逻辑是什么?遇到常见报错怎么排查?如果你正准备找一个 Python 小游戏项目练手,可以直接把这篇文章当作配套教程,跟着从头到尾实现一遍。

1. 为什么第一个项目选择贪吃蛇小游戏

1.1 练手项目怎么选

自学 Python 的过程中,练手项目的选择会直接影响学习效果的持续性。太简单的项目,比如打印九九乘法表,做完之后只是把print用得更熟练了一点;太复杂的项目,比如做一个完整的 Web 系统,知识点跨度太大,很容易在中间某个环节失去耐心。

比较合适的标准有三个:

  • 功能完整,有清晰的输入、处理和输出。
  • 单文件或少量文件就能完成,不依赖复杂的工程结构。
  • 涉及的知识点正好覆盖当前阶段的核心语法。

贪吃蛇在这三个方面都很合适。它需要用户输入(按键)、持续处理(移动逻辑)、不断输出(界面变化),并且包含循环、列表、条件判断、函数、随机数、事件响应等 Python 基础语法。做完之后,你会发现自己对列表和循环的理解明显比看教程更扎实。

1.2 贪吃蛇覆盖了哪些 Python 知识点

从学习角度看,贪吃蛇项目把 Python 基础语法中的核心部分都串起来了:

  • 列表的增删和访问。蛇身本质就是坐标点组成的列表,蛇移动相当于在列表头部插入新坐标、在尾部删除旧坐标。
  • 条件判断。方向控制、碰撞检测、食物判定,都是由一系列if条件组成的。
  • while循环。游戏的主循环负责不停检测输入、更新状态、刷新画面,这是游戏开发的核心模式。
  • 函数与模块化。把绘制、食物生成、分数显示拆成独立函数,代码才能保持清晰。
  • 事件驱动。pygame 通过事件队列处理键盘输入,这也是很多图形界面程序的通用思路。

可以说,一个贪吃蛇小游戏写下来,Python 入门阶段的核心语法基本上都实战了一遍。这种“用项目带语法”的学习方式,比单纯刷教程更容易留下印象。

1.3 最终实现的效果

这个贪吃蛇小游戏具备以下几项功能:

  • 使用方向键控制蛇头上下左右移动。
  • 蛇吃到随机出现的食物后长度增加 1,分数增加 1。
  • 蛇撞到墙壁或者撞到自己的身体,游戏结束。
  • 移动过程中禁止直接反向移动,避免蛇头瞬间撞到自己。

整个项目只有一个 Python 文件,运行方式很简单。对新手来说,它既有观赏性,又有足够的逻辑复杂度,是 Python 入门阶段性价比很高的一个实践项目。

2. 环境准备与版本说明

2.1 安装 Python

开始写代码之前,需要先确认电脑上的 Python 环境。Python 安装过程本身不难,但有两个容易踩坑的地方:一是忘记勾选“Add Python to PATH”,导致命令行里无法运行python命令;二是电脑上存在多个 Python 版本,导致pip装包时装到了别的环境。

如果还没安装 Python,建议去官网下载对应操作系统的安装包。安装过程中记得勾选 Add Python to PATH,安装完成后打开终端,输入:

python --version

能正常输出版本号,说明环境没问题。需要说明的是,不同操作系统和 Python 版本之间可能存在差异,本文示例以 Python 3 系列常见稳定版本为例,重点演示实现思路。如果执行命令提示找不到 Python,可以尝试改用python3命令。

2.2 安装 pygame

贪吃蛇需要图形界面,这里选择 pygame。pygame 是 Python 生态里非常常用的 2D 游戏开发库,内部封装了窗口创建、事件处理、图形绘制、声音播放等底层能力,非常适合用来做各类 Python 小游戏。

安装命令:

pip install pygame

如果你使用的是国内网络,可以指定镜像源加速:

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

安装完成后,在 Python 交互环境里验证:

import pygame print(pygame.version.ver)

能输出 pygame 版本号,说明安装成功。

这里补充一个建议:如果电脑上同时存在多个 Python 版本,或者准备在不同项目中使用不同版本的依赖库,尽量在虚拟环境中开发。虚拟环境可以把当前项目的依赖隔离起来,避免全局环境越来越乱。创建和使用虚拟环境的基本命令是:

python -m venv venv

激活虚拟环境后,再执行pip install pygame,这样安装的库就只属于当前项目。虽然是入门项目,但建立使用虚拟环境的习惯,对后续深入 Python 开发很有帮助。

2.3 项目目录结构

为了让思路更清楚,项目目录保持简单:

snake-game/ └── snake_game.py

整个游戏就在一个 Python 文件里实现。这样做的好处是方便直接运行、方便阅读,对于入门项目来说足够清晰。后续想扩展的时候,再根据功能拆分模块。

3. 游戏设计:先想清楚再写代码

很多新手写代码时习惯直接动手,写到一半发现逻辑乱了又推倒重来。贪吃蛇这个项目规模不大,但写之前把设计想清楚,能省下大量调试时间。

3.1 游戏规则

贪吃蛇的基本规则可以归纳为四条:

  1. 蛇在一个固定大小的矩形区域里移动。
  2. 玩家通过方向键控制蛇头方向。
  3. 蛇头碰到食物时,食物消失,蛇身长度增加,分数增加。
  4. 蛇头碰到墙壁或碰到自身身体时,游戏结束。

这里需要注意“反向移动”规则。如果蛇当前向右移动,玩家立即按左键,蛇头就会直接撞到自己的身体。实现时要做限制,比较常见的做法是不允许蛇头朝当前移动方向的反方向走。这个细节虽然小,但如果没有处理好,会出现一按反方向键就立刻判定游戏结束的问题,体验很差。

3.2 坐标与游戏界面

游戏界面本质上是一个平面坐标系。我们需要把窗口分成固定大小的格子,蛇头和食物都落在某个格子上。

本文示例的窗口大小为 600 × 600,每个格子大小为 20 × 20,因此横向和纵向各有 30 个格子。蛇身用一组格子坐标表示,例如(5, 5)表示第 5 列第 5 行的格子。

采用格子坐标而不是直接使用像素坐标,有一个明显好处:碰撞检测和蛇身增长都只需要处理整数坐标,逻辑更简单。绘制的时候再把格子坐标换算成像素坐标,既保持了游戏逻辑的清晰,又能方便调整窗口或格子大小。

3.3 数据模型设计

整个游戏只需要保存三类核心数据:

  • 蛇:一个列表,列表每一项是一个(x, y)坐标元组,列表头部是蛇头,尾部是蛇尾。
  • 方向:一个元组,例如(1, 0)表示向右,(0, -1)表示向上。
  • 食物:一个坐标元组,表示食物所在的格子。

蛇移动的过程,本质上就是两步操作:

  • 第一步:根据当前方向计算出新蛇头坐标,插入列表头部。
  • 第二步:如果没有吃到食物,就从列表尾部删除一个坐标。

如果吃到了食物,就不删除尾部坐标,蛇身长度自然增加。这也是贪吃蛇最核心的一段逻辑,理解了这两步,整个游戏的主干就理解了。

4. 逐步实现游戏核心代码

下面进入代码实现环节。为了让代码可读性更好,我会从拆解每个功能开始,最后再给出完整代码。

4.1 创建游戏窗口

pygame 的使用有几个固定的初始化步骤。先导入库,然后调用pygame.init()初始化所有模块,再用pygame.display.set_mode创建窗口。

# 文件路径:snake_game.py import pygame import random import sys # 初始化 pygame pygame.init() # 颜色(RGB 格式) BLACK = (0, 0, 0) WHITE = (255, 255, 255) GREEN = (0, 200, 0) RED = (200, 0, 0) # 窗口尺寸 WINDOW_WIDTH = 600 WINDOW_HEIGHT = 600 # 格子大小 BLOCK_SIZE = 20 # 创建窗口并设置标题 screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("贪吃蛇小游戏") # 创建时钟对象,用于控制游戏帧率 clock = pygame.time.Clock()

这段代码有几个地方值得注意。pygame.init()是使用 pygame 前必须执行的一次性初始化。颜色用 RGB 元组表示,每种颜色的数值范围是 0 到 255。后面会用到clock.tick(),它控制游戏循环的刷新频率,数字越大游戏运行速度越快。

4.2 绘制蛇与食物

游戏中需要反复绘制蛇身和食物,因此把绘制逻辑封装成函数是更清晰的做法。

def draw_block(pos, color): """在指定格子位置绘制一个色块""" rect = pygame.Rect( pos[0] * BLOCK_SIZE, pos[1] * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE ) pygame.draw.rect(screen, color, rect)

draw_block接收一个格子坐标和一个颜色,内部通过乘法把格子坐标转换成像素坐标。比如格子坐标(3, 5)在 20 × 20 的格子下,对应像素坐标是(60, 100)。

食物的生成要保证不出现在蛇身上。用random.randint随机生成坐标,然后判断这个坐标是否在蛇身列表里,如果在就重新生成:

def generate_food(snake): """随机生成一个不在蛇身上的食物坐标""" while True: x = random.randint(0, WINDOW_WIDTH // BLOCK_SIZE - 1) y = random.randint(0, WINDOW_HEIGHT // BLOCK_SIZE - 1) if (x, y) not in snake: return (x, y)

注意这里使用了WINDOW_WIDTH // BLOCK_SIZE - 1作为随机范围的上限。因为窗口宽度 600 除以格子大小 20 得到 30,坐标范围应该是 0 到 29,所以必须减 1,否则食物可能生成在窗口边界之外。

4.3 蛇的移动与按键控制

接下来是游戏主循环。pygame 程序的核心是“事件循环”:不断检查有没有窗口关闭事件、有没有按键按下事件,然后根据事件更新游戏状态。

UP = (0, -1) DOWN = (0, 1) LEFT = (-1, 0) RIGHT = (1, 0) # 蛇的初始状态 snake = [(5, 5), (4, 5), (3, 5)] direction = RIGHT food = generate_food(snake) score = 0 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if 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 != DOWN这个条件做的就是这件事。

pygame.QUIT事件对应窗口右上角的关闭按钮,收到之后调用pygame.quit()和sys.exit()退出程序。这是 pygame 程序必不可少的收尾逻辑。

4.4 碰撞检测与计分

每帧游戏循环中,先根据当前方向算出新蛇头坐标,然后做两类碰撞检测:撞墙检测和撞自己检测。如果没有碰撞,再决定蛇是否增长。

# 计算新蛇头坐标 head_x, head_y = snake[0] new_head = (head_x + direction[0], head_y + direction[1]) # 撞墙检测 grid_width = WINDOW_WIDTH // BLOCK_SIZE grid_height = WINDOW_HEIGHT // BLOCK_SIZE if new_head[0] < 0 or new_head[0] >= grid_width: print("游戏结束,撞到墙壁!") break if new_head[1] < 0 or new_head[1] >= grid_height: print("游戏结束,撞到墙壁!") break # 撞自己检测 if new_head in snake: print("游戏结束,撞到自己!") break # 蛇头移动到新位置 snake.insert(0, new_head) if new_head == food: score += 1 food = generate_food(snake) else: snake.pop()

这里的逻辑顺序很关键:先判断新蛇头是否合法,再移动蛇身,最后判断是否吃到食物。如果顺序颠倒,蛇头已经插入列表再判断碰撞,就容易出现判断错误。

撞墙或撞自己时用break跳出循环,随后程序结束。为了让玩家看清结束原因,退出前在终端输出了一句提示。后面可以进一步优化,改成在游戏窗口里显示结束画面。

4.5 完整可运行代码

把上面的部分组合起来,就是一个完整可运行的贪吃蛇小游戏:

# 文件路径:snake_game.py import pygame import random import sys pygame.init() BLACK = (0, 0, 0) WHITE = (255, 255, 255) GREEN = (0, 200, 0) RED = (200, 0, 0) WINDOW_WIDTH = 600 WINDOW_HEIGHT = 600 BLOCK_SIZE = 20 screen = pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption("贪吃蛇小游戏") clock = pygame.time.Clock() UP = (0, -1) DOWN = (0, 1) LEFT = (-1, 0) RIGHT = (1, 0) def draw_block(pos, color): """在指定格子位置绘制一个色块""" rect = pygame.Rect( pos[0] * BLOCK_SIZE, pos[1] * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE ) pygame.draw.rect(screen, color, rect) def generate_food(snake): """随机生成一个不在蛇身上的食物坐标""" while True: x = random.randint(0, WINDOW_WIDTH // BLOCK_SIZE - 1) y = random.randint(0, WINDOW_HEIGHT // BLOCK_SIZE - 1) if (x, y) not in snake: return (x, y) def show_score(score): """在窗口左上角显示当前分数""" font = pygame.font.Font(None, 36) text = font.render(f"得分: {score}", True, WHITE) screen.blit(text, (10, 10)) def main(): snake = [(5, 5), (4, 5), (3, 5)] direction = RIGHT food = generate_food(snake) score = 0 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if 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 head_x, head_y = snake[0] new_head = (head_x + direction[0], head_y + direction[1]) grid_width = WINDOW_WIDTH // BLOCK_SIZE grid_height = WINDOW_HEIGHT // BLOCK_SIZE if new_head[0] < 0 or new_head[0] >= grid_width: print("游戏结束,撞到墙壁!") break if new_head[1] < 0 or new_head[1] >= grid_height: print("游戏结束,撞到墙壁!") break if new_head in snake: print("游戏结束,撞到自己!") break snake.insert(0, new_head) if new_head == food: score += 1 food = generate_food(snake) else: snake.pop() screen.fill(BLACK) for body in snake: draw_block(body, GREEN) draw_block(food, RED) show_score(score) pygame.display.update() clock.tick(10) if __name__ == "__main__": main()

5. 运行与验证

5.1 启动游戏

在snake_game.py所在目录打开终端,执行:

python snake_game.py

如果环境配置正确,会弹出一个 600 × 600 的黑色窗口,窗口左上角显示“得分: 0”,窗口中间出现一条三节的绿色蛇和一个红色食物。

5.2 预期运行效果

  • 按上下左右方向键,蛇会朝对应方向移动。
  • 蛇吃到红色食物后,食物消失并在新位置出现,分数加 1,蛇身长度增加。
  • 蛇撞到窗口边缘或蛇头与蛇身重合时,程序在终端打印提示并结束。
  • 直接按反方向键时,方向不会改变,避免蛇立刻撞到自己。

5.3 如何调整难度

游戏速度由clock.tick(10)控制,数值越大蛇移动越快。刚运行时可能觉得速度偏慢,这是故意设置的。新手可以先在低速下观察移动和碰撞逻辑,熟悉之后再逐步提高到 15、20 甚至更高。调整一个数字就能改变游戏难度,这也是把参数集中写在代码里带来的好处。

6. 常见问题与排查思路

问题现象常见原因解决思路
ModuleNotFoundError: No module named 'pygame'pygame 未安装,或安装到了另一个 Python 环境重新执行pip install pygame,确认安装到当前解释器环境
python命令无法识别安装 Python 时未勾选 Add Python to PATH重新安装并勾选,或手动配置 PATH 环境变量
游戏窗口一闪就关闭代码运行异常或窗口创建失败在终端中运行程序,查看完整报错堆栈
按方向键没反应按键被反向移动限制拦截检查direction != DOWN这类限制条件是否正确
食物生成在窗口外随机范围上限没有减 1使用WINDOW_WIDTH // BLOCK_SIZE - 1作为randint上限
蛇移动太快或太慢clock.tick()数值不合适调整数值,游戏循环刷新频率越高蛇移动越快
pip 安装速度慢或超时网络原因导致下载不稳定使用国内镜像源安装,例如清华 PyPI 镜像

遇到报错时,建议先完整读一遍终端里的错误信息。很多初学者看到红色报错就紧张,其实 Python 的报错信息已经写明了问题文件和行号,按提示去检查对应代码,大部分问题都能自己解决。

7. 最佳实践与优化建议

写完能运行的代码只是第一步。如果想让这个 Python 小游戏项目更规范、更容易扩展,可以从几个方向继续优化。

7.1 把参数抽成配置常量

现在窗口尺寸、格子大小、移动速度、颜色都分散在代码中。更好的做法是集中在文件头部定义,例如所有颜色放在一起、所有尺寸放在一起、所有难度参数放在一起。以后想改成浅色主题、更大的窗口、更快的初始速度,只需要改几个常量,不用在代码里到处搜索。

7.2 增加游戏结束画面

目前游戏结束后直接退出,显得有点生硬。可以改成在窗口中央显示“游戏结束”和最终得分,并提示按任意键退出。这会用到 pygame 的字体渲染和延时处理,难度不高,但能明显提升游戏体验。

7.3 增加暂停与重新开始功能

对于完整的小游戏来说,暂停功能几乎是必备的。可以用空格键控制暂停状态,在游戏循环里通过一个布尔变量is_paused控制逻辑是否更新。重新开始功能则需要把蛇、方向、分数、食物全部重置为初始值。这两个功能用到的知识点还是变量、条件判断和事件处理,适合作为第一个扩展练习。

7.4 用面向对象重构

当需求变复杂时,可以考虑用面向对象的方式重构。把蛇、食物、游戏状态都设计成类,每个类负责自己的属性和行为。比如Snake类可以管理身体列表、移动方法和方向控制,Food类负责生成和位置判断。这个练习很适合巩固 Python 的类、对象、方法等概念,也是从“会写脚本”向“会设计程序”过渡的关键一步。

7.5 代码结构拆分与 Git 管理

如果继续增加功能,单个文件会越来越长。这时候可以按功能拆模块,例如snake.py负责蛇的逻辑,food.py负责食物生成,game.py负责主循环和事件处理。同时建议给项目建一个 Git 仓库,即使暂时不打算开源,本地管理版本也能让你随时回溯代码,改到一半发现方向错了可以轻松回退。

8. 下一步学习方向

贪吃蛇做完之后,可以先花点时间复盘。例如:哪些知识点原本就会,哪些是写项目时边查边学会的,哪些地方花的时间最多。花时间最多的地方往往就是你的薄弱点,记录下来,后续针对性加强。

接下来可以尝试的方向很多。比如给贪吃蛇增加障碍物和关卡,理解“游戏状态”如何随关卡切换;或者做一个打砖块、飞机大战类的小游戏,熟悉 pygame 的图片加载、音效和碰撞特效;也可以尝试用面向对象重写这个贪吃蛇,把逻辑按类拆分,锻炼代码设计能力;甚至可以把最高分保存到文件或数据库,顺便练习文件读写和简单数据持久化。

如果后续打算往后端开发方向走,可以继续夯实 Python 基础、数据结构、文件操作、异常处理,再过渡到 Web 框架。游戏项目积累的是对代码运行流程和调试能力的理解,这些能力到任何技术方向都通用。

自学 Python 最怕的不是遇到报错,而是停留在“看得懂”的舒适区。把一个小的 Python 小游戏亲手写出来、跑起来,再改出问题并解决掉,这个过程收获很大。如果你也处在 Python 入门阶段,建议打开编辑器,照着完整代码敲一遍,然后试着改几个参数观察效果变化。下一次再遇到类似的项目,你就知道该从哪里下手了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询