ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python贪吃蛇游戏开发实战:从零搭建入门项目

2026/10/2 22:58:46 拓冰建站 浏览量
Python贪吃蛇游戏开发实战:从零搭建入门项目 很多人学 Python 都会遇到同一个困境语法书看了两遍课程跟到了函数可真要自己打开编辑器却写不出一个能跑的小程序。变量、循环、列表、字典这些知识点背得出来可代码一多就不知道如何组织出了问题也不知道去哪里排查。这种学了很多还是不会做的落差恰好是贪吃蛇游戏能帮你填上的。这篇文章要给的判断很明确贪吃蛇是 Python 入门阶段性价比最高的练习项目。它不涉及复杂的算法和架构却完整覆盖了循环、条件判断、列表操作、随机数、事件驱动、坐标计算、碰撞检测这些核心基础你只需要约 100 行代码就能做成一个真正可以玩的小游戏还能立刻看到运行结果。对新手来说没有什么比自己写的代码跑起来了一个游戏更能建立正反馈。本文会从技术选型讲起逐步拆解贪吃蛇的核心逻辑提供可直接复制的完整源码再给出运行验证方法和常见问题排查清单最后补充几个实用的扩展方向。无论你是刚学完 Python 基础语法还是准备做课程设计、期末项目这篇文章都能让你带着一份能用的源码离开。1. 为什么用贪吃蛇练手它解决的难题比游戏本身更重要先说痛点。很多初学者在学完基础语法后陷入眼高手低的状态看别人的项目觉得简单自己动手却不知道第一步做什么。原因是市面上大部分习题只练单一知识点比如输出斐波那契数列统计字符串中字母出现次数这些题目的确有用但无法让你体会一个完整程序的诞生过程。贪吃蛇恰好把多个知识点粘合在一起。它需要一个数据结构来表示蛇的身体需要用循环来驱动游戏持续运行需要用条件判断来响应键盘、检测碰撞需要用随机数生成食物位置还要在手机或电脑屏幕上实时绘制画面。你不可能只靠背语法过关必须理解数据是怎么流动的逻辑分支为什么这样设计。还有一个容易被忽略的价值调试能力。贪吃蛇有明确的运行结果蛇会不会拐弯、吃食物会不会增长、撞墙会不会结束都是肉眼可见的验证标准。一旦出问题你可以一步步回放逻辑定位是坐标算错、方向判断不对还是食物与蛇重叠。这种定位问题的训练是刷几百道语法题都换不来的。这个项目适合三类人。第一类是刚学完 Python 基础、想做第一个项目的在校学生第二类是想在简历里放一个完整可运行项目的转行者第三类是已经在用 Python 做数据分析或脚本想换个方式巩固编程思维的开发者。如果你已经能轻松写几百行业务代码贪吃蛇可能对你太简单但作为教学素材或课程设计的骨架仍然有参考价值。2. 技术方案对比pygame 还是 turtle实现贪吃蛇Python 生态里有两个常见方案pygame 和 turtle。pygame 是一个专门用于游戏开发的多媒体库封装了窗口创建、图像渲染、键盘事件、声音播放等能力是 Python 2D 游戏领域的事实标准。它的核心概念包括屏幕 surface、事件循环、精灵 sprite 和时钟 clock学习曲线比 turtle 稍陡但掌握了它你就摸到了游戏开发的基本套路以后做俄罗斯方块、飞机大战都不至于从零开始。turtle 则是 Python 标准库自带的海龟绘图模块不需要安装第三方包打开 IDLE 就能用。它原本用于教学演示通过控制画笔海龟移动来画图也可以做出简化版贪吃蛇。优点是零依赖、上手极快但性能和交互能力有限不适合做正经的游戏产品。两种方案的对比可以从下面这张表看明白对比维度pygameturtle安装方式需要 pip 安装Python 内置无需安装核心定位游戏开发绘图教学事件处理完整的事件队列支持键盘、鼠标复用有基本的键盘绑定但功能简单渲染方式基于像素矩形绘制效率高基于画布图标移动画面简单代码量100 行左右结构清晰40 行能跑但扩展能力弱后续发展可以继续做复杂游戏做完贪吃蛇基本就到头了本文选择 pygame 为主方案因为它更接近真实游戏开发路径。当你把贪吃蛇用 pygame 写完后里面的主循环、事件处理、碰撞检测、物体更新等思想可以直接迁移到任何 2D 游戏里。turtle 版本更适合作为 5 分钟快速体验我们会在扩展部分提供思路但不作为全文主线。3. 环境准备与安装3.1 Python 版本要求pygame 目前支持 Python 3.8 及以上版本。只要你不是停留在远古的 Python 2 时代基本都能正常安装。建议使用官网下载的最新稳定版 Python 3安装时勾选Add Python to PATH选项这样在终端里直接输入 python 就能进入命令环境。如果你不确定自己的 Python 版本可以在终端或命令提示符里查看python --version输出类似Python 3.11.9就是可以的。如果提示找不到命令说明 Python 没有加入 PATH需要重新安装或手动配置环境变量。3.2 安装 pygame在终端中执行下面的命令即可安装 pygamepip install pygame国内用户如果下载速度慢可以使用国内镜像源加速pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后检查 pygame 是否能正常导入python -c import pygame; print(pygame.version.ver)这一步很重要。很多人写好了代码运行时报ModuleNotFoundError: No module named pygame就是跳过了安装后的验证。如果上面的命令能输出类似2.6.1的版本号说明环境已经就绪。需要特别提醒的是如果你电脑上同时装了多个 Python 版本比如系统自带 Python 和 Anaconda务必确认 pip 和 python 指向同一个解释器。判断方法很简单执行pip --version看它显示的 Python 路径是否和你python --version显示的一致。不一致的话就用python -m pip install pygame这种显式指定模块的方式安装。4. 贪吃蛇核心原理拆解在做代码之前先把游戏逻辑想清楚。一个贪吃蛇程序本质上是一个每隔一小段时间就更新一次画面的循环。这个循环里反复做四件事读取键盘输入、判断蛇是否撞墙或撞自己、决定蛇头往哪个格子移动、检查是否吃掉食物。下面逐条拆解。4.1 蛇的数据结构蛇的身体由一串连续的方格组成。在 Python 里最自然的表示方式是用列表列表的每个元素是一个(x, y)坐标元组蛇头放在列表的第 0 位蛇尾在最后一位。snake [(300, 200), (280, 200), (260, 200)]这段代码表示一条长度为 3 的蛇三节身体在 y 坐标相同的一行上蛇头在最右侧初始方向向右。用列表存蛇的好处是移动时只需要在头部插入一个新坐标再根据是否吃到食物决定要不要删掉尾部。插入和删除都发生在列表两端性能足够。4.2 移动规则蛇的移动不是每一节都挪一下而是蛇头前进一格身体其他部分跟上。用列表实现时移动规则非常清晰把新蛇头坐标插入到列表头部然后弹出列表最后一个元素。新蛇头坐标等于当前蛇头坐标加上方向向量。方向向量用(dx, dy)表示向右是(20, 0)向左是(-20, 0)向上是(0, -20)向下是(0, 20)20 是格子边长。如果下一步吃到了食物蛇要长一节那么插入蛇头后不删除尾部即可。这个先插头、按需删尾的技巧是整个游戏的精华理解了它也就理解了链式数据结构的常见操作。4.3 食物生成与计分食物同样用一个坐标元组表示生成时要在窗口范围内随机选一个没有被蛇占据的格子。这里的一个隐藏坑是如果蛇已经很长食物随机生成在蛇身上的概率会增大。所以不能只随机一次要用循环不断重试直到选到一个空白格子为止。计分规则简单直接每吃一个食物加 1 分。分数显示在窗口左上角每次食物被吃掉后刷新。4.4 碰撞检测碰撞检测是游戏结束条件分两种一是蛇头坐标超出窗口边界即撞墙二是蛇头坐标出现在蛇身列表的其余部分里即碰到自己。注意判断时要先算新蛇头坐标再判断它是否合法不能先移动再判断否则会出现画面已经越界但还没结算的闪烁。很多初学者在写碰撞检测时会把撞自己判断写成if head in snake[1:]其实在移动逻辑中如果吃食物时不删尾部snake里包含的是当前帧移动前的蛇身头部坐标不可能和原有蛇身重叠除非发生了反向移动。所以简单使用if head in snake也是安全的但为了严谨和可读性推荐判断head in snake[1:]因为它明确地排除了蛇头自身。4.5 游戏主循环pygame 的事件驱动模型决定了程序不能是从头到尾跑一遍就退出的顺序结构而是一个while running的死循环。每一轮循环完成三件事遍历事件队列处理用户操作调用移动与碰撞逻辑更新数据调用绘图函数把最新状态渲染到窗口。为了让蛇移动速度合适还要用clock.tick(FPS)控制循环频率比如每秒循环 10 次蛇就每秒走 10 格。这个主循环是 pygame 程序的骨架几乎所有 2D 游戏都是这个结构。你会在这篇文章的完整源码里再次看到它。5. 完整源码与逐行解析5.1 项目文件结构项目非常简单只需要一个 Python 文件你可以把它命名为snake_game.py放在任意目录下。后续如果需要增加音效、图片等功能再考虑拆分模块。文件结构如下所示snake_game/ └── snake_game.py5.2 完整可运行代码下面这一段是完整的贪吃蛇游戏源码复制保存为snake_game.py即可运行。代码尽量保持初学者友好关键逻辑都有注释。# 文件名snake_game.py import pygame import random # 初始化 pygame pygame.init() # 窗口与网格配置 WINDOW_WIDTH 600 WINDOW_HEIGHT 400 GRID_SIZE 20 # 一个格子的边长像素 CELL_SIZE GRID_SIZE # 蛇身与食物的大小与格子对齐 FPS 10 # 初始速度每秒 10 格 # 颜色定义RGB BLACK (0, 0, 0) GREEN (0, 255, 0) RED (255, 0, 0) WHITE (255, 255, 255) # 创建窗口和时钟 screen pygame.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT)) pygame.display.set_caption(Python 贪吃蛇) clock pygame.time.Clock() font pygame.font.Font(None, 36) # 默认字体字号 36 def spawn_food(): 在空白网格中随机生成食物 while True: x random.randrange(0, WINDOW_WIDTH, CELL_SIZE) y random.randrange(0, WINDOW_HEIGHT, CELL_SIZE) if (x, y) not in snake: return (x, y) def init_game(): 初始化游戏状态重新开始游戏时调用 global snake, direction, next_direction, food, score, game_over snake [ (WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2), (WINDOW_WIDTH // 2 - CELL_SIZE, WINDOW_HEIGHT // 2), (WINDOW_WIDTH // 2 - 2 * CELL_SIZE, WINDOW_HEIGHT // 2), ] direction (CELL_SIZE, 0) # 初始方向向右 next_direction direction food spawn_food() score 0 game_over False def handle_keydown(key): 处理按键方向键改变方向空格键重新开始 global next_direction if game_over: if key pygame.K_SPACE: init_game() return if key pygame.K_UP and direction ! (0, CELL_SIZE): next_direction (0, -CELL_SIZE) elif key pygame.K_DOWN and direction ! (0, -CELL_SIZE): next_direction (0, CELL_SIZE) elif key pygame.K_LEFT and direction ! (CELL_SIZE, 0): next_direction (-CELL_SIZE, 0) elif key pygame.K_RIGHT and direction ! (-CELL_SIZE, 0): next_direction (CELL_SIZE, 0) def move_snake(): 根据方向移动蛇头处理吃食物和游戏结束 global direction, next_direction, snake, food, score, game_over # 防止同帧内多次按键导致反向自撞 if (next_direction[0] * -1, next_direction[1] * -1) direction: next_direction direction direction next_direction head_x snake[0][0] direction[0] head_y snake[0][1] direction[1] # 撞墙判断 if head_x 0 or head_x WINDOW_WIDTH or head_y 0 or head_y WINDOW_HEIGHT: game_over True return head (head_x, head_y) # 撞自己判断排除蛇头自身 if head in snake[1:]: game_over True return # 蛇头前插如果吃到食物就不删尾部否则删尾部 snake.insert(0, head) if head food: score 1 food spawn_food() else: snake.pop() def draw(): 绘制背景、蛇、食物和文字 screen.fill(BLACK) for segment in snake: pygame.draw.rect(screen, GREEN, (segment[0], segment[1], CELL_SIZE, CELL_SIZE)) pygame.draw.rect(screen, RED, (food[0], food[1], CELL_SIZE, CELL_SIZE)) score_surface font.render(fScore: {score}, True, WHITE) screen.blit(score_surface, (10, 10)) if game_over: over_surface font.render(Game Over! Press SPACE to restart, True, WHITE) over_rect over_surface.get_rect(center(WINDOW_WIDTH // 2, WINDOW_HEIGHT // 2)) screen.blit(over_surface, over_rect) pygame.display.flip() # 启动游戏 init_game() running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: handle_keydown(event.key) if not game_over: move_snake() draw() clock.tick(FPS) pygame.quit()5.3 关键函数解释spawn_food()的功能是生成食物坐标。random.randrange(0, WINDOW_WIDTH, CELL_SIZE)会返回从 0 到 600 之间、步长为 20 的随机数确保食物和蛇身都在同一个网格上不会出现半格重叠。while True循环会一直尝试直到食物位置不在蛇身上。init_game()负责每次游戏开始或重新开始时重置状态。这里使用global声明是因为我们需要修改模块级别的变量。如果你愿意用snake [...]之类的局部变量也可以把整个游戏封装成类但为了初学阶段保持线性阅读本文采用全局变量方案。handle_keydown(key)处理方向键和空格键。一个常见的错误是让用户在同一帧内连续按两个方向键比如当前向右先按上再按左最终方向为左但蛇头方向实际上是向右的左转本身不违法但如果你继续先按上再按左再按下就可能在蛇头未转向时先改变为下导致反向撞到自己。因此在move_snake()的开始我们做了一个额外检查如果next_direction正好是direction的反方向就把next_direction重置为direction只接受不反向的改变。这个细节虽然多了一行代码但对游戏手感影响很大。move_snake()是核心移动逻辑。先用方向向量算出新蛇头坐标再判断撞墙和撞自己都不满足条件时插入蛇头。注意snake[1:]的切片排除了蛇头这是一个比较严谨的写法避免了极端情况下头部坐标和旧身体重叠造成误判。draw()负责绘制所有元素。先screen.fill(BLACK)清屏再画蛇的每一节、食物、分数。pygame.display.flip()把绘制好的内容一次性提交到屏幕。pygame 采用双缓冲机制每次修改画面后都要调用flip或update。6. 运行步骤与效果验证6.1 运行游戏在snake_game.py所在目录打开终端执行python snake_game.py如果一切正常会弹出一个 600 乘 400 的黑色窗口标题为Python 贪吃蛇窗口内有一条绿色蛇和一个红色食物。使用键盘的上、下、左、右方向键控制蛇头移动蛇会自动向前走Slower 或 Faster 可以自己改FPS值来调整。6.2 预期效果与成功标准用下面几个标准来判断游戏是否成功蛇会匀速向右移动不按键时蛇头不会停下。按方向键后蛇头改变方向身体跟着拐弯。蛇头碰到食物后食物消失蛇身长度增加 1左上角分数增加 1新食物出现在随机空白格。蛇头撞到窗口边界时游戏停止中央显示Game Over! Press SPACE to restart。按下空格键后蛇和食物重置分数归零游戏重新开始。6.3 运行失败排查顺序如果窗口没有出现先看终端里有没有报错信息。常见错误包括ModuleNotFoundError: No module named pygame说明 pygame 未安装或安装到了别的 Python 环境。pygame.error: video system not initialized通常是因为没有调用pygame.init()或pygame.display.set_mode被放在错误位置。窗口一闪而过可能是while running循环没有进入或pygame.QUIT事件处理有语法错误。排查时优先看终端输出。绝大多数问题都可以通过阅读报错信息的前几行定位不需要盲目猜测。7. 常见问题与排查方法在实际学习和运行过程中有几个问题出现频率非常高这里用表格给出排查思路问题现象可能原因排查方式解决方案运行报错No module named pygamepygame 未安装或安装到其他 Python 环境执行pip --version确认路径用python -m pip install pygame显式安装窗口一闪而过程序立即退出主循环条件判断错误或 import 路径问题在pygame.quit()前加print(exit)调试检查while running是否进入确认事件处理逻辑按方向键没有反应键盘事件没有进入handle_keydown或方向判断错误在handle_keydown第一行打印 key 值确认事件类型是pygame.KEYDOWN且没有和pygame.QUIT混淆蛇可以瞬间反向然后撞死同帧内连续按键方向判断只基于当前方向在move_snake()中加反向检查使用next_direction的反向保护如本文代码所示食物生成在蛇身上随机位置没有检查是否与蛇重叠查看spawn_food是否使用while循环重试改为while (x, y) in snake:循环生成蛇移动太快或太慢FPS设置不合适修改FPS的值测试通常在 8 到 15 之间调整游戏结束后按空格不重启game_over状态没有被正确重置检查init_game()是否被调用在handle_keydown中处理K_SPACE并调用init_game()除了表格中的问题还有一个新手经常会遇到的坑在移动逻辑里先删尾部再插入头部导致蛇身断裂或长度初始变化。正确的顺序一定是先插头再按需删尾。如果把这两步写反了不仅蛇看起来像在倒退还会在吃到食物时错误地少一节。8. 扩展思路与工程建议贪吃蛇写出来能跑只是第一步。要让它变得更好玩、更适合放进简历还可以做下面几种扩展。8.1 调整游戏速度默认FPS 10是相对新手友好的速度。你可以把FPS改成8或15感受不同的手感。更高级的做法是让速度随着分数动态提升呈现越吃越快的挑战感。改动位置在move_snake()吃到食物加分之后可以加一句clock.tick(min(10 score // 3, 25))不过这句话不应该放在move_snake()内部因为clock.tick在主循环末尾调用。更规范的做法是在主循环里根据分数动态计算速度while running: # 事件处理省略 current_speed min(10 score // 3, 25) clock.tick(current_speed)这里的min用来限制最大速度避免游戏快到无法操作。8.2 增加障碍物在窗口预先画出几个固定障碍物蛇碰到障碍物同样游戏结束。障碍物可以用一个列表表示obstacles [(100, 100), (120, 100), (140, 100), (160, 100)]然后在move_snake()的碰撞判断里增加一个if head in obstacles: game_over True。在draw()里把障碍物画成灰色矩形即可。这个扩展虽然代码量不大但能显著提升游戏复杂度。8.3 代码组织与模块化当游戏功能增加后全局变量和函数会越来越多。建议把代码拆成几个模块比如settings.py保存配置game.py放逻辑函数main.py作为入口。不过拆分模块的前提是你已经能在一份文件里理清全局变量之间的关系。对新手而言先维护一份文件反而是更好的练习。更推荐的一种改进是把游戏封装成类。用__init__初始化所有属性用handle_event、update、draw方法实现游戏循环的三个阶段。这样做的好处是避免了global声明变量之间的依赖关系通过self清晰可见。当你开始写类版本时你对 Python 面向对象编程的理解也会上一个台阶。8.4 安全与性能注意事项运行 pygame 游戏时避免在循环内部做耗时的文件读写或大计算否则会拖慢帧率。在开发新版时建议保留一个可调整的FPS变量方便测试。如果游戏突然卡顿优先检查是不是食物生成逻辑陷入了死循环——当蛇身几乎占满窗口时spawn_food的重试次数会急剧增加。这种情况下可以加一个最大重试次数限制或者用维护空白格子列表的方式替代随机重试。游戏代码本身不涉及敏感数据但在开发过程中仍然建议遵守基本规范不要用eval处理玩家输入不要把路径写死如果游戏需要保护源码或处理用户数据再加一层合理的安全策略。9. 总结与后续学习方向现在你手里应该有了一个能运行的 Python 贪吃蛇项目。回顾整个实现你真正掌握的不只是贪吃蛇怎么写而是一套通用的编程思维用列表维护连续对象用方向向量控制移动用事件循环驱动交互用碰撞检测处理边界和失败条件。这套思维可以平移到俄罗斯方块、飞机大战、井字棋等几乎所有 2D 小游戏上。下一步我建议你认真做三件事。第一把代码从头到尾逐行读一遍给每个函数写下注释直到你能不看源码画出游戏的执行流程图第二尝试增加一个新功能比如障碍物、双人模式、AI 自动寻路在改代码的过程中你会更深刻地理解数据结构的作用第三把这份代码放进你的课程设计或项目作品集然后去挑战一个更完整的项目比如飞机大战或 2048。如果你在做扩展时遇到困难优先回到本文的常见问题与排查方法一节大部分问题都出在事件处理、碰撞判断和列表更新顺序这三处。继续写下去很快你就会发现游戏开发的门槛远没有想象中那么高。