2026/7/26 9:51:28

Python Pygame贪吃蛇游戏开发:从零到一掌握游戏循环与碰撞检测

Python Pygame贪吃蛇游戏开发:从零到一掌握游戏循环与碰撞检测 1. 项目概述为什么用Python和Pygame做贪吃蛇是绝佳入门项目如果你刚学Python或者想找个项目练手把经典的贪吃蛇游戏做出来绝对是个不会错的选择。这项目听起来简单但“麻雀虽小五脏俱全”它几乎涵盖了游戏开发最核心的几个概念游戏循环、事件处理、图形绘制、碰撞检测和状态管理。用Python来做语法简洁上手快用Pygame这个库它把底层那些复杂的图形和声音接口都封装好了你只需要关注游戏逻辑本身不用从零开始造轮子。我见过很多新手一上来就想搞个复杂的网络游戏或者3D大作结果被各种概念和工具链劝退。贪吃蛇不一样它的规则人人都懂目标明确——控制一条蛇吃食物变长别撞墙也别咬到自己。这个明确的目标能让你把精力集中在“如何用代码实现”这个核心问题上。当你看到自己写的代码让一个方块在屏幕上动起来吃到“食物”后变长那种成就感是看十遍教程都换不来的。这不仅仅是写了个游戏更是完成了一次从想法到可运行程序的完整闭环对建立编程自信至关重要。所以无论你是想巩固Python基础语法还是初次接触游戏开发这个项目都值得你花上几个小时亲手敲一遍。接下来我会带你从零开始用最清晰的思路和详细的注释一步步构建出这个游戏。我们不止要让它能跑起来还要让你明白每一行代码背后的“为什么”。2. 环境准备与工具选型搭建高效舒适的开发环境工欲善其事必先利其器。在开始写代码之前我们需要把“战场”布置好。这里会涉及Python解释器、Pygame库以及一个顺手的代码编辑器。别小看这一步一个配置得当的环境能极大提升你的开发效率和排错能力。2.1 Python解释器安装与配置首先你需要一个Python。我强烈建议使用Python 3.8或以上的版本它们更稳定对新库的支持也更好。去Python官网下载安装程序是最稳妥的方式。安装时请务必勾选“Add Python to PATH”这个选项。这个操作相当于给系统装了个“导航”让你在命令行CMD或终端的任何位置都能直接输入python或pip来启动解释器或安装包。很多新手卡在第一步就是因为没勾这个选项导致后面各种“命令找不到”的错误。安装完成后验证一下。打开你的命令行Windows上是CMD或PowerShellmacOS或Linux上是终端输入python --version。如果能看到类似Python 3.8.10的版本信息恭喜你第一步成功了。如果提示“不是内部或外部命令”那就需要手动去系统环境变量里添加Python的安装路径具体步骤网上教程很多核心就是把Python安装目录比如C:\Users\你的用户名\AppData\Local\Programs\Python\Python38和它的Scripts子目录添加到系统的PATH变量里。注意一台电脑上可以安装多个Python版本但容易引起混乱。如果你是纯粹的新手建议只安装一个版本。如果之前有安装但不确定可以在命令行输入where pythonWindows或which python3macOS/Linux来查看当前调用的是哪个。2.2 Pygame库的安装Pygame不是Python自带的它是一个第三方库需要用包管理工具pip来安装。打开命令行输入以下命令pip install pygame如果网络通畅这条命令会自动从Python的官方包索引下载并安装Pygame及其依赖。安装成功后可以进入Python交互模式命令行输入python回车验证一下import pygame print(pygame.ver)如果没有报错并且打印出版本号比如2.5.2说明安装成功。这里有个常见坑点如果你同时安装了Python2和Python3那么pip命令可能对应的是Python2的包管理器。为了确保给Python3安装可以使用pip3 install pygame。更稳妥的方法是使用python -m pip install pygame这个命令会明确使用当前python命令对应的pip。2.3 代码编辑器的选择VSCode的快速配置写代码你需要一个趁手的编辑器。Notepad太简陋专业的IDE如PyCharm对新手可能稍显复杂。我推荐微软的Visual Studio CodeVSCode它轻量、免费、插件生态丰富对Python支持极好。下载安装VSCode从官网下载安装即可。安装Python扩展打开VSCode点击侧边栏的扩展图标或按CtrlShiftX搜索“Python”找到由Microsoft发布的那个点击安装。这个扩展提供了代码高亮、智能提示IntelliSense、代码格式化、调试等核心功能。配置Python解释器在VSCode中打开你的项目文件夹然后按CtrlShiftP打开命令面板输入“Python: Select Interpreter”选择你刚才安装的Python版本。这样VSCode就知道用哪个Python来运行和调试你的代码了。可选但推荐安装Code Runner扩展同样在扩展商店搜索“Code Runner”并安装。安装后你可以在代码文件上右键选择“Run Code”或者点击右上角的小三角按钮快速运行当前Python脚本非常方便。环境搭好了我们终于可以开始构思游戏的核心了。3. 游戏核心设计拆解贪吃蛇的每一个部件在动手敲代码前我们得先在脑子里把游戏拆解清楚。贪吃蛇游戏主要包含哪些“演员”和“舞台”它们各自有什么属性又要遵守什么规则想明白了这些写代码就是水到渠成的事情。3.1 游戏窗口与坐标系Pygame的游戏都是在一個窗口Surface里绘制的。我们需要先初始化这个窗口并定义它的大小比如800像素宽600像素高。在Pygame的坐标系里窗口的左上角是原点(0, 0)x轴向右延伸y轴向下延伸。这一点和数学坐标系不同需要适应。我们将这个窗口想象成一个网格棋盘。为了简化我们通常不会让蛇以单个像素为单位移动那样太快且难以控制。更常见的做法是引入“网格”或“块”Block的概念。比如我们将整个窗口划分为20x15个格子具体数值可调每个格子的大小是40x40像素。那么蛇的身体和食物就都是以一个40x40的方块为单位出现在这些格子上。蛇每次移动就是从当前格子移动到相邻的格子。这样游戏逻辑就从一个连续的像素空间简化到了一个离散的网格空间逻辑判断比如是否吃到食物、是否撞墙会变得非常简单只需要比较网格坐标是否相等即可。3.2 游戏主角蛇的抽象与实现蛇是这个游戏的绝对主角。我们需要用数据来精确地描述它身体蛇的身体由一系列连续的方块网格坐标组成。我们可以用一个列表list来存储这些坐标列表的第一个元素是蛇头最后一个元素是蛇尾。例如snake_body [(5, 5), (4, 5), (3, 5)]表示蛇头在网格(5,5)第二节在(4,5)尾巴在(3,5)。方向蛇当前前进的方向。通常用四个常量表示上(UP)、下(DOWN)、左(LEFT)、右(RIGHT)。我们可以用元组来表示方向向量例如(0, -1)表示向上y坐标减1(1, 0)表示向右x坐标加1。移动逻辑每一帧或每一次游戏循环蛇会朝着当前方向移动一格。具体操作是在蛇头位置的前方根据方向向量计算生成一个新的坐标将这个新坐标插入到身体列表的头部作为新的蛇头然后去掉列表尾部的坐标蛇尾向前移动了一格。如果吃到了食物则不去掉尾部这样身体长度就增加了。绘制遍历身体列表中的每一个坐标将其乘以方块的像素大小如40转换到实际的屏幕像素坐标然后绘制一个矩形比如绿色即可。3.3 游戏目标食物的生成与判定食物是蛇成长的目标。它的逻辑相对简单位置一个随机的网格坐标比如(random.randint(0, 19), random.randint(0, 14))确保在20x15的网格范围内。生成规则游戏开始时生成一个每当蛇头坐标与食物坐标重合即吃到食物时蛇身变长然后在空白位置不能与蛇身重叠重新生成一个新的食物。绘制根据食物的网格坐标计算屏幕像素坐标绘制一个不同颜色比如红色的方块。3.4 游戏规则碰撞检测与状态管理游戏规则决定了胜负核心是碰撞检测撞墙检测判断蛇头的x坐标是否小于0或大于等于网格最大列数或者y坐标是否小于0或大于等于网格最大行数。如果是则游戏结束。自撞检测判断蛇头的坐标是否出现在蛇身体列表的剩余部分即从第二项开始切片。如果是说明蛇头撞到了自己的身体游戏结束。吃食物检测判断蛇头坐标是否等于食物坐标。如果是则分数增加蛇身变长不移除尾部并重新生成食物。游戏还需要一个状态机来管理不同的场景比如“进行中”、“游戏结束”、“暂停”等。我们可以用一个变量如game_state来记录当前状态游戏循环会根据这个状态来决定是更新游戏逻辑、显示结束画面还是等待用户重新开始。4. 分步实现从零搭建游戏骨架理论说得差不多了现在打开你的编辑器新建一个文件比如叫snake_game.py我们开始一行行把游戏构建起来。我会把代码分成几个逻辑块并配上详细的注释。4.1 初始化与常量定义任何Pygame程序都从初始化开始。我们先导入必要的模块定义一些常量比如颜色、屏幕尺寸、网格大小、方块尺寸等。把这些“魔法数字”定义为常量能让代码更清晰以后想调整游戏外观也只需改一个地方。import pygame import random import sys # 初始化pygame pygame.init() # 定义颜色 (R, G, B) BLACK (0, 0, 0) WHITE (255, 255, 255) GREEN (0, 255, 0) RED (255, 0, 0) BLUE (0, 120, 255) # 蛇头可以用不同颜色区分 # 游戏窗口和网格参数 GRID_SIZE 40 # 每个方块的像素大小 GRID_WIDTH 20 # 网格的列数 GRID_HEIGHT 15 # 网格的行数 SCREEN_WIDTH GRID_SIZE * GRID_WIDTH # 屏幕宽度 方块大小 * 列数 SCREEN_HEIGHT GRID_SIZE * GRID_HEIGHT # 屏幕高度 方块大小 * 行数 # 方向常量用向量表示 UP (0, -1) DOWN (0, 1) LEFT (-1, 0) RIGHT (1, 0) # 游戏状态 RUNNING 0 GAME_OVER 1 PAUSED 24.2 创建游戏窗口与时钟接下来我们创建游戏窗口并设置一个时钟对象来控制游戏帧率。帧率FPS决定了游戏画面每秒更新的次数太高会消耗过多资源太低会感觉卡顿。对于贪吃蛇这种游戏10-15帧每秒FPS就足够了这样蛇的移动速度不会太快玩家也有时间反应。# 创建游戏窗口 screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(贪吃蛇 - Python Pygame 制作) # 创建时钟对象用于控制游戏帧率 clock pygame.time.Clock() FPS 12 # 帧率控制蛇的移动速度4.3 初始化游戏数据现在初始化游戏的核心数据蛇、食物、方向、分数和状态。def init_game(): 初始化或重置游戏数据 # 蛇的初始身体三个方块水平排列。列表第一个元素是蛇头。 snake [ (GRID_WIDTH // 2, GRID_HEIGHT // 2), # 蛇头在屏幕中心 (GRID_WIDTH // 2 - 1, GRID_HEIGHT // 2), # 第二节 (GRID_WIDTH // 2 - 2, GRID_HEIGHT // 2) # 第三节尾巴 ] direction RIGHT # 初始方向向右 # 生成第一个食物确保不在蛇身上 food generate_food(snake) score 0 game_state RUNNING return snake, direction, food, score, game_state def generate_food(snake_body): 在空白位置随机生成食物 while True: # 随机生成一个网格坐标 food_pos (random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)) # 如果这个坐标不在蛇身上就返回它 if food_pos not in snake_body: return food_pos4.4 核心游戏循环与事件处理游戏循环是游戏的心脏它永不停止直到退出每一轮循环都做以下几件事处理用户输入、更新游戏状态、绘制当前画面。我们用一个while True循环来实现。def main(): snake, direction, food, score, game_state init_game() while True: # ---------- 1. 事件处理 ---------- for event in pygame.event.get(): if event.type pygame.QUIT: # 点击窗口关闭按钮 pygame.quit() sys.exit() # 彻底退出程序 # 键盘按下事件 if event.type pygame.KEYDOWN: if game_state RUNNING: # 方向键控制蛇的转向这里有个关键逻辑不能直接反向 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 elif event.key pygame.K_p: # 按P键暂停 game_state PAUSED elif game_state PAUSED: if event.key pygame.K_p: # 暂停时再按P继续 game_state RUNNING elif game_state GAME_OVER: if event.key pygame.K_r: # 游戏结束时按R键重新开始 snake, direction, food, score, game_state init_game() # ---------- 2. 游戏状态更新 ---------- if game_state RUNNING: # 根据当前方向计算新的蛇头位置 head_x, head_y snake[0] dir_x, dir_y direction new_head (head_x dir_x, head_y dir_y) # 碰撞检测是否撞墙 if (new_head[0] 0 or new_head[0] GRID_WIDTH or new_head[1] 0 or new_head[1] GRID_HEIGHT): game_state GAME_OVER continue # 跳过本次循环的绘制直接显示结束画面 # 碰撞检测是否撞到自己 if new_head in snake: game_state GAME_OVER continue # 将新的蛇头插入身体列表最前面 snake.insert(0, new_head) # 碰撞检测是否吃到食物 if new_head food: score 10 food generate_food(snake) # 生成新食物 # 注意吃到食物不移除尾部蛇身变长 else: # 没吃到食物移除尾部蛇身长度不变 snake.pop() # ---------- 3. 绘制 ---------- # 用黑色填充整个屏幕相当于清屏 screen.fill(BLACK) # 绘制网格线可选让网格更清晰 for x in range(0, SCREEN_WIDTH, GRID_SIZE): pygame.draw.line(screen, (40, 40, 40), (x, 0), (x, SCREEN_HEIGHT)) for y in range(0, SCREEN_HEIGHT, GRID_SIZE): pygame.draw.line(screen, (40, 40, 40), (0, y), (SCREEN_WIDTH, y)) # 绘制蛇 for i, (grid_x, grid_y) in enumerate(snake): # 将网格坐标转换为屏幕像素坐标 rect pygame.Rect(grid_x * GRID_SIZE, grid_y * GRID_SIZE, GRID_SIZE, GRID_SIZE) # 蛇头用蓝色身体用绿色 color BLUE if i 0 else GREEN pygame.draw.rect(screen, color, rect) # 给身体方块画个边框更有立体感 pygame.draw.rect(screen, (0, 200, 0), rect, 1) # 绘制食物 food_rect pygame.Rect(food[0] * GRID_SIZE, food[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE) pygame.draw.rect(screen, RED, food_rect) # 给食物画个内嵌效果 pygame.draw.rect(screen, (150, 0, 0), food_rect, 3) # 绘制分数 font pygame.font.SysFont(None, 36) # 使用系统默认字体大小36 score_text font.render(fScore: {score}, True, WHITE) screen.blit(score_text, (10, 10)) # 将文字绘制到屏幕左上角(10,10)位置 # 根据游戏状态绘制提示文字 if game_state PAUSED: pause_text font.render(PAUSED (Press P to Resume), True, WHITE) text_rect pause_text.get_rect(center(SCREEN_WIDTH//2, SCREEN_HEIGHT//2)) screen.blit(pause_text, text_rect) elif game_state GAME_OVER: game_over_text font.render(GAME OVER! Press R to Restart, True, WHITE) text_rect game_over_text.get_rect(center(SCREEN_WIDTH//2, SCREEN_HEIGHT//2)) screen.blit(game_over_text, text_rect) # ---------- 4. 刷新屏幕 ---------- pygame.display.flip() # 将绘制好的内容更新到屏幕上 # ---------- 5. 控制帧率 ---------- clock.tick(FPS) # 确保循环每秒最多执行FPS次 if __name__ __main__: main()把以上所有代码块按顺序组合起来保存为snake_game.py然后在命令行进入文件所在目录运行python snake_game.py你的贪吃蛇游戏就应该能跑起来了5. 核心机制深度解析与优化技巧代码能运行只是第一步。要让游戏体验更好代码更健壮我们需要深入理解一些关键机制并做一些优化。5.1 方向控制与输入处理中的“防抖”在事件处理部分我们有一个关键逻辑禁止蛇直接反向移动。例如当蛇向右移动时不能立即按左键让它向左否则蛇头会瞬间掉头撞上自己的身体第二节导致游戏立即结束。这是贪吃蛇游戏的基本规则。我们的代码if event.key pygame.K_UP and direction ! DOWN:就实现了这个规则。但这里有个潜在的“bug”或说体验问题如果玩家快速连续按下两个方向键比如先按左键立刻又按上键由于Pygame事件队列的处理速度很快有可能在同一帧内处理了两个KEYDOWN事件。虽然我们的逻辑防止了直接反向但极端快速的输入可能导致非预期的转向。一个更稳健的做法是不在事件处理中直接改变direction而是设置一个“下一帧方向”的变量。在游戏状态更新的部分再用这个“下一帧方向”去安全地更新当前方向。这样可以确保一帧内只发生一次有效的方向改变。# 优化思路示例需修改部分代码 next_direction direction # 初始化 # 在事件循环中 if event.key pygame.K_UP and direction ! DOWN: next_direction UP # ... 其他方向同理 # 在游戏状态更新部分开始移动前 direction next_direction # 然后再用direction去计算new_head这个技巧在需要更精确输入控制的游戏中很常见在我们的贪吃蛇里因为帧率较低FPS12通常感觉不到问题但知道这个原理对以后做更复杂的游戏有帮助。5.2 游戏循环与帧率控制的精妙之处clock.tick(FPS)这行代码至关重要。它做了两件事1) 暂停当前循环确保从上一次调用tick到现在时间至少过去了1/FPS秒2) 返回实际经过的时间毫秒。这保证了游戏逻辑更新的频率是稳定的不会因为电脑快慢而导致蛇的移动速度发生变化。这就是所谓的“基于时间的游戏循环”。在我们的简单实现中蛇的移动速度直接由FPS控制。FPS12意味着每秒更新12次游戏逻辑蛇每秒移动12格。如果你想实现“蛇移动速度随着长度增加而变快”的效果就不能单纯提高FPS那会让所有动画都变快。一个更专业的做法是引入一个独立的“游戏刻”game tick计时器。例如设置一个变量move_timer每次主循环都累加实际经过的时间clock.tick(FPS)的返回值或pygame.time.get_ticks()当这个累加值超过你设定的移动间隔如200毫秒时才执行一次蛇的移动和碰撞检测逻辑然后重置计时器。这样绘制帧率(FPS)和逻辑更新频率就可以解耦。5.3 绘制优化与视觉增强我们目前的绘制代码在每一帧都重绘了整个屏幕screen.fill(BLACK)对于这样一个小游戏完全没问题。但要知道Pygame的draw函数是有开销的。如果游戏元素非常多可以考虑“脏矩形”更新即只重绘屏幕上发生变化的部分。不过对于贪吃蛇“全量重绘”最简单可靠。我们可以做一些简单的视觉增强来提升体验渐变色蛇身遍历蛇身列表时可以根据索引计算一个渐变的颜色值让蛇从头部到尾部颜色逐渐变深更有立体感。食物动画让食物微微闪烁或跳动。可以在食物方块上叠加一个大小周期性变化的圆形或者交替绘制两种深浅不同的红色。移动平滑效果高级我们现在的蛇是“瞬移”到下一个格子的。如果想实现平滑移动就需要存储每个方块的实际像素坐标浮点数每帧根据方向和速度进行插值绘制时也使用浮点数坐标。这会大大增加逻辑复杂度但对于学习动画原理是个很好的练习。6. 常见问题排查与功能扩展即使代码逐行对照运行时也可能遇到各种问题。这里总结几个常见坑点和解决方案。6.1 运行时报错与解决方案速查表错误现象可能原因解决方案ModuleNotFoundError: No module named pygamePygame库未安装或未安装到当前Python环境。在命令行使用pip install pygame或python -m pip install pygame安装。确认安装的Python和运行脚本的Python是同一个。窗口一闪而过或打开后立即关闭。代码可能在初始化或循环早期就遇到错误退出了。在命令行运行脚本查看具体的错误信息。常见于导入失败、语法错误或pygame.init()失败。按键无反应蛇不动。1. 事件循环没写对比如用了pygame.event.wait()而不是pygame.event.get()。2. 方向键判断条件写错例如direction ! DOWN写成了direction ! UP。3. 游戏状态game_state不是RUNNING。1. 确保使用for event in pygame.event.get():来遍历所有事件。2. 仔细检查方向键事件处理中的逻辑条件。3. 在事件处理中添加print(event)或检查game_state的值来调试。蛇可以穿过自己的身体或反向。方向控制逻辑有漏洞没有正确禁止反向操作。确保每个方向键的判断都包含了“不能直接反向”的条件如按“上”键时当前方向不能是“下”。食物生成在了蛇的身体上。generate_food函数中的循环可能因为蛇身填满整个屏幕而陷入死循环。在generate_food函数中添加保护逻辑计算空白格子数量如果为0蛇已占满屏幕则返回一个特殊值或直接判定玩家胜利。游戏运行卡顿或速度异常快。没有使用clock.tick(FPS)或FPS值设置不合理。确保在主循环末尾调用了clock.tick(FPS)并将FPS调整到一个合理的值如10-15。6.2 功能扩展让你的贪吃蛇与众不同基础版本完成后你可以尝试添加更多功能这既是练习也能让游戏更好玩难度递增每得100分将FPS值增加1或者减少蛇的移动间隔时间让游戏越来越有挑战性。障碍物模式在初始化时在屏幕上随机生成一些不可通过的墙壁黑色方块。蛇撞上墙壁也会游戏结束。这需要修改碰撞检测和食物生成逻辑食物也不能出现在墙上。多种食物引入不同颜色的食物有的加1分但让蛇加速有的加5分但让蛇减速几秒有的甚至能让蛇缩短一节。游戏关卡设计多个关卡地图每个地图有不同的障碍物布局。通关一个地图后加载下一个。音效与背景音乐使用pygame.mixer模块在吃食物、撞墙、游戏结束时播放不同的音效。最高分记录将最高分保存到一个本地文本文件如highscore.txt中每次游戏开始和结束时读取和更新它。6.3 项目打包与分享当你完成了一个满意的版本可能想分享给不会安装Python的朋友。你可以使用PyInstaller将脚本打包成独立的.exe文件Windows或可执行程序macOS/Linux。首先安装PyInstallerpip install pyinstaller然后在命令行中进入你的脚本所在目录执行pyinstaller --onefile --windowed snake_game.py--onefile将所有依赖打包成一个单独的可执行文件。--windowed运行时不显示命令行窗口对于图形程序推荐使用。命令执行成功后会在dist文件夹下找到生成的可执行文件。你可以把这个文件发给别人他们双击就能运行你的贪吃蛇游戏了。注意打包后的文件可能会比较大几十MB因为它包含了Python解释器和所有需要的库。走到这一步你不仅完成了一个游戏更走完了一个小型软件项目的完整生命周期从环境搭建、设计构思、编码实现、调试排错到最终打包分发。这个过程中积累的经验远比游戏本身更有价值。