Python+Pygame实现贪吃蛇:核心算法与游戏开发实战

发布时间:2026/10/17 1:26:32
Python+Pygame实现贪吃蛇:核心算法与游戏开发实战
不需要从“贪吃蛇还能吃什么”这种废话开始直接聊正事。作为一个写过好几个小游戏、也带过不少人入门编程的老手我越来越觉得贪吃蛇就是练手项目的天花板。它麻雀虽小五脏俱全有状态管理、有碰撞检测、有实时输入处理、有渲染循环还有一个完整的“游戏结束”判定逻辑。你用纯Python写一遍再用Pygame写一遍最后再用面向对象重构一遍基本就把游戏开发的底层套路摸透了。这篇文章我就拿“python实现简单的snake game”这个项目把从零到能玩的全过程拆开讲。不跟你说空话每一步都是我自己实际跑过的连踩过的坑都一并列出来。适合刚学完Python基础、想拿小项目检验自己的人也适合想快速上手Pygame、但又不想只看官方文档的读者。1. 项目整体设计与思路拆解1.1 先想清楚再做贪吃蛇的核心需求是什么拿到项目别急着敲代码先把需求列明白。一个最简单的贪吃蛇至少要有这几样东西一条会动的蛇能响应上下左右按键改变运动方向。一个随机生成的食物蛇头碰到食物就算吃到。吃到食物后蛇变长分数增加。蛇头撞到墙壁或撞到自己身体游戏结束。就这四条别的都是锦上添花。很多人写贪吃蛇写着写着就失控了就是因为上来就想做“带菜单、带音效、带排行榜”的全功能版本结果连蛇都动不起来。我的建议是第一版只做上面四条跑通了再慢慢加。这里有个很关键的认知贪吃蛇的“蛇身跟随”效果不是让每一节身体都记住上一节的位置而是用一种叫“队列滑动”的思路。蛇移动的每一帧你只需要做两件事在头部方向加一个新格子同时把尾巴那一格删掉。吃到食物的时候不删尾巴就行了。“删掉尾巴、添加新头”这六个字就是整个贪吃蛇的核心算法一句话就讲清楚了。1.2 为什么选Pygame而不是turtle或tkinterPython里能做图形界面的库不少turtle适合画线条tkinter适合做表单类界面但做实时游戏Pygame是最合适的选择。Pygame本质上是SDL的Python封装底层用C写的性能完全扛得住2D小游戏。贪吃蛇这种量级的画面Pygame跑到每秒几十帧毫无压力。更重要的是Pygame把游戏开发里常用的东西都帮你封装好了窗口创建、事件循环、键盘监听、矩形绘制、图像加载、声音播放全都有现成的接口。我用turtle写过一次贪吃蛇体验很不舒服。turtle的绘图模型是“画笔在画布上移动”你要画一个方块得自己记录坐标再逐一填充颜色蛇身的刷新效率也很低。tkinter的问题则是事件循环和主循环容易冲突做实时游戏总感觉拖泥带水。Pygame里你写一个while循环每一帧都重新绘制画面这才是做游戏的标准姿势。提示如果你只是想验证“我会写贪吃蛇”用turtle也行。但如果你想练的是“游戏开发的基本套路”直接上Pygame省得以后重写一遍。1.3 模块划分不追求过度设计但要有基本的分层我见过不少人交上来的贪吃蛇代码就是一个300行的单文件从头到尾平铺直叙主循环里又管事件、又管逻辑、又管画图看着就头疼。这种代码跑起来没问题但你想加个暂停功能、加个难度选择就无从下手了。我的建议是分三个模块配置、游戏逻辑、主循环。配置模块放常量比如窗口尺寸、格子大小、颜色RGB值、蛇的初始速度全放在一个地方改起来方便。游戏逻辑模块处理蛇的移动、食物的生成、碰撞的判断这部分是纯算法不依赖Pygame。主循环模块负责初始化Pygame窗口、处理键盘事件、调用游戏逻辑并把结果画到屏幕上。这样分不是为了装模作样玩架构而是为了好调试。蛇撞墙的判断逻辑有问题你在纯逻辑模块里用print就能测不用反复打开游戏窗口。这个体验在写复杂版本的时候尤其实用。2. 环境准备与基础框架搭建2.1 装好依赖三步验证环境没问题写Pygame项目第一步自然是装库。用pip装命令很简单pip install pygame如果你用的是虚拟环境记得先激活虚拟环境再装。我习惯给每个小程序建一个独立的环境免得系统里的Python被各种依赖搞乱。装完之后验证一下环境是否正常新建一个小脚本跑一下import pygame pygame.init() win pygame.display.set_mode((800, 600)) pygame.display.set_caption(测试窗口) while True: for event in pygame.event.get(): if event.type pygame.QUIT: pygame.quit() exit()能弹出窗口并且按关闭按钮能正常退出就说明环境没问题可以开始写正式代码了。这一步别跳很多人的问题其实出在装库失败或者版本冲突上先验证能省不少排查时间。2.2 定义全局配置把常量集中起来管理我写贪吃蛇时会先定义一个config区域把所有需要调整的参数集中放在一起# 窗口与网格 SCREEN_WIDTH 600 SCREEN_HEIGHT 600 GRID_SIZE 20 GRID_WIDTH SCREEN_WIDTH // GRID_SIZE GRID_HEIGHT SCREEN_HEIGHT // GRID_SIZE # 颜色RGB BLACK (0, 0, 0) WHITE (255, 255, 255) GREEN (0, 200, 0) RED (200, 0, 0) # 游戏参数 INIT_SPEED 10为什么用600乘600因为格子大小取20时宽高方向各能整除出30个格子计算方便画面比例也协调。如果你想把画面调大只需要改一个常量其他代码都不用动。网格化坐标是贪吃蛇的一个细节关键。格子大小20意味着蛇的每一节身体都占据一个20乘20像素的矩形。蛇移动一格就是20像素而不是随便一个像素值。网格化的好处是碰撞判断和坐标计算都变成整数运算简单且可靠。2.3 初始化Pygame窗口并搞定游戏主循环Pygame项目都有一个固定的生命周期初始化窗口、进入主循环、循环内处理事件和逻辑、退出时释放资源。贪吃蛇的主循环骨架大概是这样的pygame.init() screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(Snake Game) clock pygame.time.Clock() running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False # 游戏逻辑更新 # 重新绘制画面 screen.fill(BLACK) # 绘制食物和蛇 pygame.display.flip() clock.tick(INIT_SPEED) pygame.quit()这个循环里clock.tick(INIT_SPEED)控制的是帧率也就是每秒刷新多少帧。帧率10意味着蛇每秒移动10个格子也就是10格/秒。如果你觉得蛇太快就把这个数字调小想挑战高难度就调大。帧率和速度是同一个概念因为贪吃蛇的移动全靠帧刷新来驱动这一点跟很多讲究“物理帧率”的游戏不太一样。注意pygame.display.flip()和pygame.display.update()都能把画面更新到屏幕上区别是flip会刷新整个窗口update可以只刷新指定区域。贪吃蛇这种小型游戏用flip最简单省事。3. 蛇、食物与碰撞核心逻辑逐段解析3.1 蛇的数据结构选择为什么用列表蛇可以用一个列表来表示每个元素是一个坐标如[x, y]。列表的第一个元素是蛇头后面的元素依次是蛇身。在Pygame里坐标通常是从左上角开始的x向右增大y向下增大。这点新手容易忽略——y轴方向是向下的跟数学里常见的坐标系正好相反。初始化时蛇不需要太长三节身体就够了放在屏幕中间偏左的位置。初始方向我习惯设为向右因为大部分人的惯用手和直觉都是先按右键。snake [] start_x GRID_WIDTH // 2 start_y GRID_HEIGHT // 2 for i in range(3): snake.append([start_x - i, start_y])这段代码生成蛇的初始状态蛇头在屏幕中心身体向左依次延伸两节。方向默认为向右。注意这里用的是[start_x - i, start_y]生成的结果是蛇头在最右边、身体在左边正好对应向右移动的初始姿态。3.2 移动逻辑“插头去尾”算法详解蛇的每一帧移动按这个步骤来根据当前方向计算新的蛇头坐标。把新蛇头插入列表头部。判断是否吃到食物吃到则不删尾部没吃到则删掉尾部。写成代码就是def move_snake(snake, direction, food_pos, is_eaten): head snake[0].copy() if direction UP: head[1] - 1 elif direction DOWN: head[1] 1 elif direction LEFT: head[0] - 1 elif direction RIGHT: head[0] 1 snake.insert(0, head) if not is_eaten: snake.pop() return snake这个“插头去尾”的思路有一个好处蛇的每一节不用互相传递位置信息因为每次移动都相当于整条蛇在同一个方向上平移了一格。蛇身会完整地跟随蛇头的轨迹。这里有个非常容易犯的错直接修改蛇头坐标会导致蛇身不连贯。比如你写成snake[0][0] 1那么蛇头是动了但身体还留在原地整条蛇就断开了。正确的做法是先复制蛇头坐标生成新坐标再插入列表头部。因为列表存的是坐标值head copy后即使原来的蛇头坐标后续被修改新插入的蛇头也不会受影响。3.3 方向控制与“禁止掉头”防呆设计方向控制是新手翻车最多的地方。最容易出的问题有两个一是没做禁止掉头按了左键又按右键蛇直接原地“反杀”自己二是按键读取的时机不对导致方向响应迟钝或者丢失。先说禁止掉头。这其实是一行代码的逻辑如果当前方向是RIGHT那么新的方向不能是LEFT其他方向都可以依次类推。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这段逻辑必须在事件循环里处理而不是放在主循环外面。很多人问“为什么我按键没反应”多半是因为把按键判断写在了事件循环之外的某个地方Pygame根本接收不到事件。再说方向响应的时机。如果这一帧蛇头已经向左移动了然后你又在这一帧里按了向上下一帧蛇头就会向上移动。听起来没问题但极端情况下会出现“一帧两键”的bug假设当前方向是UP你在同一帧内先按了LEFT又按了DOWN而没有方向状态锁的话第二个按键会把方向从LEFT改成DOWN结果蛇直接掉头向下撞到自己。解决思路是加入一个“方向锁”只要这一帧已经处理过一个方向键就忽略后续的方向键。但Pygame的事件循环是一次性把这一帧的所有事件都取出来的一个简单的做法是用一个direction_changed布尔值在每一帧开始时重置为False处理按键时如果已经改过方向就跳过。3.4 食物生成随机定位与“不与蛇重叠”的排查法食物本质是一个坐标点。随机生成很简单用random.randint在网格范围内取一个坐标就行import random def generate_food(snake): while True: x random.randint(0, GRID_WIDTH - 1) y random.randint(0, GRID_HEIGHT - 1) if [x, y] not in snake: return [x, y]用while循环取随机坐标直到不在蛇身上看似粗暴实际非常高效。因为格子数量是有限的大部分时候一两次就能命中。只要蛇没有占满整个屏幕这个循环的期望次数都非常低。有人问网格坐标和像素坐标的换算这里要注意一下。random.randint(0, GRID_WIDTH - 1)返回的是网格坐标比如(15, 15)在绘制时你得把它换算成像素坐标x乘GRID_SIZE等于像素xy乘GRID_SIZE等于像素y。绘制食物时用pygame.draw.rect(screen, RED, (food_x * GRID_SIZE, food_y * GRID_SIZE, GRID_SIZE, GRID_SIZE))就能画出一个跟格子同样大小的红色方块。3.5 碰撞判定自己写函数别依赖库碰撞检测有两种思路。一种是直接用Pygame的Rects碰撞函数另一种是自己判断坐标是否重叠。我推荐自己写判断原因有两个一是逻辑简单清晰二是为后续加游戏状态做铺垫。蛇撞墙的判断看蛇头坐标是否超出网格边界即可def hit_wall(snake): x, y snake[0] if x 0 or x GRID_WIDTH or y 0 or y GRID_HEIGHT: return True return False蛇撞自己看蛇头是否与身体其他部分重叠def hit_self(snake): head snake[0] return head in snake[1:]注意head in snake[1:]这里的in运算符对列表是逐个比较的贪吃蛇几十节身体的情况下性能毫无问题根本不需要用set做优化。但有一个细节容易踩坑如果蛇头坐标恰好和身体里的某一节重合在旧坐标下这是不可能的因为蛇身和蛇头不可能重叠。真正会触发撞自己的场景是蛇头移动到了一个新的格子而这格子里恰好有身体这就说明蛇绕过了一个完整的环正好撞上了它自己。4. 实操过程与关键环节打磨4.1 从零搭好主程序一次完整的合并前文的模块拆开讲似乎很散把它们合到一起完整骨架如下import pygame import random SCREEN_WIDTH 600 SCREEN_HEIGHT 600 GRID_SIZE 20 GRID_WIDTH SCREEN_WIDTH // GRID_SIZE GRID_HEIGHT SCREEN_HEIGHT // GRID_SIZE BLACK (0, 0, 0) WHITE (255, 255, 255) GREEN (0, 200, 0) RED (200, 0, 0) INIT_SPEED 10 def generate_food(snake): while True: pos [random.randint(0, GRID_WIDTH - 1), random.randint(0, GRID_HEIGHT - 1)] if pos not in snake: return pos def main(): pygame.init() screen pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption(Snake Game) clock pygame.time.Clock() 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 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 head snake[0].copy() if direction UP: head[1] - 1 elif direction DOWN: head[1] 1 elif direction LEFT: head[0] - 1 elif direction RIGHT: head[0] 1 snake.insert(0, head) # 判断吃到食物 if head food: score 1 food generate_food(snake) else: snake.pop() # 碰撞检测 if head[0] 0 or head[0] GRID_WIDTH or head[1] 0 or head[1] GRID_HEIGHT: running False if head in snake[1:]: running False # 绘制 screen.fill(BLACK) pygame.draw.rect(screen, RED, (food[0] * GRID_SIZE, food[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE)) for segment in snake: pygame.draw.rect(screen, GREEN, (segment[0] * GRID_SIZE, segment[1] * GRID_SIZE, GRID_SIZE, GRID_SIZE)) pygame.display.flip() clock.tick(INIT_SPEED) pygame.quit() if __name__ __main__: main()这段代码已经是一个能玩的贪吃蛇了。吃一个食物得一分撞墙或撞身体就退出。把这段跑熟之后再往里面加功能就容易多了。4.2 画面表现力方块中间留一条缝蛇身立刻变高级我早期写的贪吃蛇蛇身是一整块整块的绿色方块头尾相连远远看去像一个绿色的大色块不好看。后来发现一个很简单的技巧每个方块绘制时不要填满整个格子留2像素的间距。for segment in snake: x, y segment[0] * GRID_SIZE 2, segment[1] * GRID_SIZE 2 pygame.draw.rect(screen, GREEN, (x, y, GRID_SIZE - 4, GRID_SIZE - 4))这样蛇身每一节之间就有明显的分隔线看起来像一节一节的比一整块好看很多。食物的方块同样可以留缝。这个改动画质提升的效果非常直观可以说是“一行代码提升游戏质感”的典范。4.3 游戏启动画面与结束画面不弹黑窗口的直接体验很多小游戏做出来双击运行就是一个黑窗口游戏结束后窗口直接关闭玩家根本来不及看自己得了多少分。想要体验上一个档次加个结束画面很重要。结束画面的核心就是游戏结束时不立刻退出窗口而是显示一段“Game Over”的文字和一个分数等玩家按任意键或关闭窗口才退出。实现起来不复杂把主循环拆分两个阶段playing状态和game_over状态。用一个game_active布尔值控制game_active True game_over False while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False elif event.type pygame.KEYDOWN: if game_over: # 任意键重新开始这里简化为直接退出 running False if game_active: # 原有游戏逻辑 # 绘制 if game_over: # 显示Game Over和得分 pass这样玩家能看清最终得分体验会好很多。如果你还想加“按空格重新开始”那你需要在game_over状态下重置蛇和食物的坐标并把方向、分数全部初始化一遍相当于是重开一局。4.4 加速机制与难度曲线让游戏越玩越刺激一个固定的10帧/秒高手玩到30分之后就会发现毫无挑战。给贪吃蛇加难度最直接的办法是每吃几个食物就提高帧率。score 0 speed INIT_SPEED # 每吃一个食物 score 1 if score % 3 0: speed 1把clock.tick(speed)里的速度换成这个动态speed游戏会每隔3分加快一点。这个方案的曲线比较平缓适合大部分玩家。你想更硬核的话可以让速度按指数或者线性函数增长。但注意帧率别加太猛超过25帧每秒之后蛇的移动会快到人眼反应不过来就失去游戏乐趣了。我习惯把最高速度限制在20超过就封顶不然疯狂的蛇会瞬间撞墙玩家只会觉得是bug而不是难度。5. 常见问题与排查技巧实录5.1 食物生成在蛇身体内部到底是谁的错新手最容易遇到的现象食物突然出现在蛇身上或者食物一直不出现。原因几乎都是食物生成的坐标跟蛇的坐标重叠了。虽然generate_food里用了while循环避开蛇身但如果你调用它的时机不对就可能出问题。举个例子如果你在蛇移动之前就调用generate_food而蛇还没移动那食物可能生成在蛇当前占据的位置上等蛇一移动食物就“出现在”身体上了。正确的时机是蛇移动并插入新头之后再判断是否跟食物重叠。上面完整代码里的顺序就是正确的。还有一个小概率事件当蛇几乎占满整个屏幕时generate_food的while循环会尝试很多次如果格子几乎全被蛇占了理论上是死循环。正常游戏中这种情况很少见但理论上存在。想要严谨的话可以在生成前判断一下蛇的长度是否等于格子总数减一如果是直接判断获胜结束游戏。5.2 方向键冲突为什么按了左边蛇却往上走这个问题我在前面提过这里再说一个场景假设蛇正在向右移动你在很短的时间里先按了上又按了左。如果代码在事件循环里依次处理最终方向会变成LEFT蛇头没有转向上而是直接转向左。这在操作上非常容易导致“自杀”蛇向右移动时上方的身体刚好堵住了左转的路你连按两键之后蛇一头扎进自己身体。解决方案就是我前面说的“本帧方向锁”。在事件循环开头设一个direction_changed False处理方向键时先判断它True就跳过。这样可以保证一帧内只响应一次方向变化。但同时要注意太严格的方向锁会带来不跟手的操作感。你会感觉按键反应迟钝因为连续快速按两个方向时第二个方向会被吞掉。这里需要在“防止误操作”和“操作手感”之间做权衡。我的经验做法是不锁整帧而是只禁止明显掉头的方向即上下左右四个方向上只禁止反向其他方向都允许。这样操作很跟手又不会出现180度掉头。5.3 蛇身越来越长但画面闪烁刷新区域问题玩的过程中画面偶尔闪一下大部分情况下是因为绘制时用了整屏刷新而整屏刷新的时机和逻辑更新不同步。Pygame双缓冲模式下display.flip()的调用频率跟主循环一致理论上不该闪。真正导致闪烁的常见原因是你把绘制代码写在了逻辑代码之前导致画面先绘制了旧数据再更新逻辑数据下一帧又绘了新数据视觉上就是一卡一卡的。解决方法是严格遵循“先逻辑、再绘制”的顺序每一帧都把逻辑更新完毕后再一次性绘制整个画面。另外如果你在绘制时用了pygame.draw.rect一个接一个地画蛇身画了第一节再画第二节在浅色背景下是看不出来的。但如果你给每节身体加了不同的颜色或者特效就可能看到“画了一半”的画面。这时候可以考虑先把所有要画的矩形放到一个list里最后统一用pygame.display.update(rect_list)刷新效率会更高。5.4 用pygame的Rect碰撞函数导致的误判有人偷懒想用snake[0].colliderect(food_rect)来判断是否吃掉食物结果发现蛇头跟食物“擦肩而过”的时候明明没接触到也会判定吃到。原因在于Rect对象记录的是整数坐标而蛇头移动的步长是一格20像素两个矩形只要挨着就可能被算作重叠。我的建议是贪吃蛇这种网格游戏不要走像素碰撞那一套直接用“坐标相等”来判断。因为蛇的每一步都是精确的一格你只要判断head [food_x, food_y]是否成立就行。这个判定比像素碰撞简单一百倍也永远准确。5.5 游戏结束却不断弹窗报错一个很常见的问题游戏结束后while循环退出但你又调用了pygame.quit()然后接着还想绘制画面就会报错“video system not initialized”。原因是pygame.quit()会把所有Pygame子系统关闭包括显示和事件模块。解决方案是只在真正要退出整个程序时才调用pygame.quit()游戏逻辑上用running False退出主循环即可。退出主循环后你可以再显示一个end画面或者直接退出。先退出Pygame再绘制是典型的顺序错误。5.6 常见问题速查表问题现象原因解决方案蛇身不跟随蛇头移动身体不动或断裂直接修改蛇头坐标而不是插入新头部复制坐标生成新头insert到列表头部按键无响应按方向键蛇不动KEYDOWN判断写错位置或事件循环消耗掉了把KEYDOWN分支放在事件循环内食物在蛇身体上食物跟蛇重叠生成时机在蛇移动前在移动后、绘制的间隙生成新食物反向掉头立即死亡快速按反向键导致撞自己没有禁止180度转向加方向判断当前右不能接左当前上不能接下速度越来越快但上限太高蛇快到无法控制帧率无限增长设置速度上限比如最高20帧/秒窗口关闭后报错关闭窗口后卡死或提示已退出退出顺序错误主循环结束只设置running False最后统一pygame.quit()画面闪烁绘制和逻辑不同步逻辑更新和绘制交叉出错严格先逻辑后绘制每个主循环只绘制一次6. 实战经验总结与扩展方向6.1 我踩过最大的坑面向对象过度设计第一次给贪吃蛇做重构时我写了一大堆类Snake类、Food类、Game类、Renderer类每个类几十行代码包含各种继承和接口最后发现调试一个移动逻辑要跳四五个文件效率极其低下。后来我学乖了小项目就用小项目的方式一个大主循环加上几个纯函数就够了。面向对象不是不行但物极必反。贪吃蛇这种规模的项目用类反而增加了认知负担。真正值得抽象成模块的是逻辑层和渲染层而不是把所有东西都塞进对象里。如果你确实想练面向对象我建议按这样的粒度拆一个Game类只负责整体流程内部含蛇列表、食物坐标、方向和分数一个Snake类只负责移动和碰撞方法但绘制逻辑仍然留在Game里因为Pygame的绘制函数跟窗口耦合太深硬抽出去反而是个累赘。6.2 扩展建议从贪吃蛇到下一个游戏贪吃蛇这个项目不是写完就完事了它是一块非常好的跳板。我推荐的延伸方向有三个按难度递增第一个方向是给贪吃蛇加“穿墙模式”或“障碍物”穿墙即蛇从一边边界进入后会从另一边出来障碍物则是地图上生成几个固定的墙壁撞上就死。这个改动主要是碰撞逻辑的调整代码量不大但玩法变化极大。第二个方向是加多玩家支持。最简单的双人模式就是两条蛇在同一张地图上各自按键控制先撞到对方或撞墙的输。这个需要引入多状态管理对“状态分离”的锻炼很有价值。第三个方向是做一个完整的“重启-暂停-切换难度”菜单。这需要加入一个状态机从主菜单到游戏中到暂停再到结束每个状态有一个draw函数和一个update函数。这个锻炼的思路正好是做任何一个复杂游戏都需要的框架能力。6.3 小技巧用配置文件管理游戏参数如果你打算把贪吃蛇改造成一个可以分享给同学的成品我强烈建议你写一个config.py文件把所有的常量放到里面然后主程序用from config import *导入。这样别人想改窗口大小、改格子颜色、改初始速度只需要改一个文件完全不用碰主逻辑代码。这是个好习惯虽然对贪吃蛇这种规模来说有点过度但养成习惯对以后写大项目是加分项。6.4 关于编码规范的一点碎碎念贪吃蛇虽然是入门项目但代码风格从一开始就值得认真对待。变量名别用a、b、c这种用snake_head、food_pos这类自解释的名字。函数也不要叫f1、f2叫generate_food、hit_wall就有意义。我批改过不少初学者的贪吃蛇作业最常见的问题不是bug多因为功能就那些而是代码没法看。写100行代码的人反而比写300行代码的人更容易让人看懂因为前者每行都是必需的。记住你写的代码是给之后的人包括一周后的自己看的不是给机器看的机器只要能跑就行而人要能看懂才叫真的完成。6.5 最后说说我对贪吃蛇这个项目的看法很多人觉得贪吃蛇太简单不值得认真做我恰恰相反。正因为简单它才适合你把每一个细节都做透。做一遍你能掌握Pygame的基础渲染和事件处理。做两遍你能理解游戏循环和碰撞检测。做三遍你能体会模块划分与状态管理。同一个练习题做三遍每一遍都有新的收获这是贪吃蛇这个项目最大的价值所在。我在实际调试中体会最深的一点是一个看着简单的“插头去尾”逻辑在不同状态下吃到食物、没吃到食物、撞墙、撞自己会衍生出完全不同的行为。只有自己亲手跑了代码才能真正理解这些状态怎么转换。代码写得再熟悉不运行、不调试、不犯错这个项目对你的价值就损失了一大半。所以别急着把代码背下来亲手敲一遍故意写错几个地方再看看自己能不能修好。这个过程比任何教程都管用。