ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Python贪吃蛇实战:用turtle模块从零实现第一个小游戏

Python贪吃蛇实战:用turtle模块从零实现第一个小游戏 简介一款基于Python tkinter库实现的贪吃蛇游戏源码面向希望通过实际项目巩固基础的Python初学者也适合作为教学演示案例项目过程完整覆盖了GUI界面搭建、键盘事件监听、游戏循环、碰撞检测等关键知识点。压缩包内共有2个文件其中snakegame.py为可直接运行的游戏主程序Arial.ttf为配套界面字体整个压缩包仅162KB下载解压后即可使用。目前已有1832人学习过这个项目是适合入门的练手参考。游戏功能完整包含蛇的方向控制、食物随机生成、碰撞判定、蛇身增长和得分显示代码附有清晰注释采用tkinter的Canvas绘制画面、after方法定时刷新方便理解事件驱动与游戏状态管理。读者既能获得带中文注释的完整源码与字体文件又能在此基础上扩展难度等级、增加障碍物或自定义界面样式整个项目从运行效果到代码结构都适合模仿学习对提升Python编程能力很有帮助。 聊到Python练手项目贪吃蛇才是真正的入门常青树。不管你是刚把Python装好、还在跟基础语法搏斗的新手还是想找个小项目把变量、循环、条件判断、函数、事件响应这些概念串起来复习一遍的开发者Python贪吃蛇这个小游戏都能用很低的学习成本让你完整走一遍“拆需求—写代码—调通运行”的实战流程。下面我会用Python标准库自带的turtle模块从零搭一个能跑的贪吃蛇逐段拆解每一步的设计思路和关键代码。就算你完全没接触过turtle照着敲也能把第一版跑起来。1. 项目概述与核心需求解析1.1 为什么贪吃蛇是Python入门的经典项目贪吃蛇能成为入门神作核心在于它把程序设计的基础知识点压缩在一个逻辑非常清晰的小游戏里。蛇的移动帮你理解坐标变化和循环食物的随机生成让你快速上手random模块蛇身增长操作列表碰撞检测练坐标比较与条件判断键盘控制则是最直观的事件监听示例。这些恰好是很多入门教程前几章讲完、但一直找不到场景练手的内容等你真正写完一版贪吃蛇这些概念就从“看懂了”变成了“会用了”。网上搜“贪吃蛇源码”能搜出一大片但很多版本要么写得乱要么依赖第三方库对环境不熟的新手并不友好。我这版只依赖Python标准库turtle目标就三个能看懂、能运行、能扩展。等你把这版跑通后面想换pygame、想加关卡、想记录最高分都有了可以落地的起点。1.2 核心规则拆解把游戏规则翻译成程序逻辑动手写代码之前先把规则梳理清楚。贪吃蛇的基本规则其实就四条蛇在一个固定大小的方形区域里自动前进玩家通过方向键控制它转向食物随机出现在区域里蛇头吃到食物后蛇身加长、分数增加蛇头撞到墙壁或者撞到自己的身体游戏结束。翻译成程序语言就是四件事第一用一个二维坐标系表示游戏区域蛇头每一帧沿当前方向前进一格第二监听键盘输入按键改变蛇头的前进方向第三生成随机坐标作为食物检测蛇头坐标与食物坐标的距离第四每帧更新后判断蛇头坐标是否越界、是否与蛇身坐标重叠。把这几件事拆清楚后面的代码基本就是照着需求逐行填内容。1.3 环境准备Python安装与VSCode配置开始写代码前先确认Python已经装好。去python.org下载对应系统的安装包安装时务必勾选“Add Python to PATH”这是新手环境里最常见的坑。装完后打开终端Windows下按WinR输入cmd回车macOS或Linux直接打开Terminal输入python --version能打印出版本号就说明装好了。如果这一行什么都没输出大概率是没勾PATH重装一次并仔细确认即可。编辑器推荐VSCode装好Python插件后新建一个.py文件写好代码直接按F5就能运行。如果你用PyCharm也完全没问题这篇文章不依赖任何IDE特性。另外强调一句turtle是Python自带的标准库不需要额外pip安装只要Python环境正常import turtle就能用这也是我选它而不是pygame的原因之一。2. 整体设计与技术选型2.1 Turtle还是Pygame怎么选才不踩坑写贪吃蛇之前先要面对一个选择用turtle还是pygame我把两者的特点整理成了下面的对比表。对比项turtlepygame安装方式Python标准库自带需要pip install pygame适合层级入门教学、简单图形程序正规小游戏、动画项目事件处理绑定键盘回调简洁事件队列更接近游戏引擎绘制能力简单矢量图形图像、精灵、音频、碰撞检测学习曲线低看几行就懂较高需要理解surface、sprite等概念如果目标是第一次完整跑通一个游戏turtle是更省事的选择。它的坐标系和画布逻辑非常直观很快能写一个可运行版本成就感来得快。turtle的局限性后面可以换pygame补但那是升级路线不是入门阶段该纠结的事。2.2 程序架构一个简单的游戏循环不管用什么库贪吃蛇的核心结构都是一个持续运行的主循环每轮循环里做三件事处理输入、更新状态、绘制画面。turtle默认在每次图形成像时自动刷新画布图形多了会闪所以我在初始化时调用window.tracer(0)关闭自动刷新然后在每轮循环里手动window.update()这种“手动控制渲染时机”的思路和真正的游戏引擎是相通的。键盘输入用事件监听方式处理window.onkeypress(函数, 键名)按下方向键时修改蛇头的方向变量。注意不能在游戏循环里用input()阻塞等待输入因为循环要持续跑输入则是异步事件两者必须分开处理。2.3 设计里的几个关键取舍写这版贪吃蛇时有几个细节值得单独说说。第一蛇和食物的基本单位统一用20像素蛇头每帧移动20像素与turtle默认方块尺寸对齐碰撞判定用“距离小于20”作为重叠条件简单可靠。第二方向控制里加了“防掉头”判断当前方向是up时不允许按down否则蛇头会瞬间反向撞进自己身体这是新手最容易踩的坑。第三食物随机坐标范围取-260到260步长20既保证食物不会跑到墙边去又能确保蛇头以20像素步长移动时刚好踩中。这些取舍在游戏里只是几行代码但积累下来的设计思路可以迁移到更大的项目里。3. 实操过程与代码实现3.1 初始化窗口与游戏角色第一步先创建窗口、蛇头和食物。三个对象的初始化逻辑几乎一样创建turtle对象、设置形状和颜色、抬起画笔防止移动时画线、放到指定坐标。import turtle import time import random window turtle.Screen() window.title(Python贪吃蛇) window.bgcolor(black) window.setup(width600, height600) window.tracer(0) snake_head turtle.Turtle() snake_head.speed(0) snake_head.shape(square) snake_head.color(green) snake_head.penup() snake_head.goto(0, 0) snake_head.direction stop food turtle.Turtle() food.speed(0) food.shape(circle) food.color(red) food.penup() food.goto(0, 100)window.setup把画布设为600乘600坐标系原点在正中间四个方向最远处分别是正负300。speed(0)表示动画速度最快penup()保证移动时不留下轨迹。direction变量用来记住蛇头当前方向初始设为stop等玩家按键后再开始移动。3.2 键盘事件与蛇的移动逻辑蛇的移动是整个游戏最核心的部分先看关键代码。def go_up(): if snake_head.direction ! down: snake_head.direction up def move(): x snake_head.xcor() y snake_head.ycor() if snake_head.direction up: y 20 elif snake_head.direction down: y - 20 elif snake_head.direction left: x - 20 elif snake_head.direction right: x 20 snake_head.goto(x, y) window.listen() window.onkeypress(go_up, Up)方向函数里带了防掉头判断比如当前方向是up时不能按down这个判断必须写不然蛇头会直接反向撞进自己身体。move函数没有用setx和sety分两次移动而是先算出新坐标最后一次性goto到目标位置这样能避免移动过程中产生斜向轨迹。3.3 食物生成与蛇身增长食物随机生成只需要一个函数def spawn_food(): food.goto(random.randint(-13, 13) * 20, random.randint(-13, 13) * 20)随机范围取-13到13乘20后就是-260到260刚好在600乘600的窗口内。蛇身用列表snake_body保存每次吃到食物就在蛇尾位置追加一个新方块然后在主循环里让每一节身体依次移动到前一节的位置视觉上蛇就“长”了一节。这里有个细节容易被忽略新方块的位置如果随便放会在画布上突然冒出一个灰格子观感很怪。所以我在追加时先把它定位到当前蛇尾的位置这样增长过程就是自然延长没有瞬移感。3.4 碰撞检测与游戏结束判断碰撞检测分为撞墙和撞自己两部分。窗口边界是正负300蛇头宽20所以越界条件设成大于280或小于-280留一点余量视觉效果上更像“撞到墙了”。撞墙之后用“复位式重置”蛇头回到原点方向恢复stop所有蛇身移出画面外列表清空分数归零。蛇身移到画面外的技巧是把它们全部goto到(1000, 1000)比直接隐藏更稳妥因为后面还要复用这些对象。if snake_head.xcor() 280 or snake_head.xcor() -280 or snake_head.ycor() 280 or snake_head.ycor() -280: time.sleep(1) snake_head.goto(0, 0) snake_head.direction stop for segment in snake_body: segment.goto(1000, 1000) snake_body.clear() score 0撞自己的检测就是遍历蛇身列表判断蛇头与每一节身体的距离是否小于20一旦成立同样执行一次重置逻辑。3.5 完整可运行的贪吃蛇代码上面所有片段拼起来再加一个计分文字显示就是可以直接运行的一整版。下面这份代码我实际跑过逻辑上没问题你新建一个snake.py文件贴进去就能玩。import turtle import time import random score 0 delay 0.1 window turtle.Screen() window.title(Python贪吃蛇) window.bgcolor(black) window.setup(width600, height600) window.tracer(0) snake_head turtle.Turtle() snake_head.speed(0) snake_head.shape(square) snake_head.color(green) snake_head.penup() snake_head.goto(0, 0) snake_head.direction stop food turtle.Turtle() food.speed(0) food.shape(circle) food.color(red) food.penup() food.goto(0, 100) snake_body [] score_text turtle.Turtle() score_text.speed(0) score_text.color(white) score_text.penup() score_text.hideturtle() score_text.goto(0, 270) score_text.write(f得分: {score}, aligncenter, font(Arial, 16, normal)) def go_up(): if snake_head.direction ! down: snake_head.direction up def go_down(): if snake_head.direction ! up: snake_head.direction down def go_left(): if snake_head.direction ! right: snake_head.direction left def go_right(): if snake_head.direction ! left: snake_head.direction right window.listen() window.onkeypress(go_up, Up) window.onkeypress(go_down, Down) window.onkeypress(go_left, Left) window.onkeypress(go_right, Right) def move(): x snake_head.xcor() y snake_head.ycor() if snake_head.direction up: y 20 elif snake_head.direction down: y - 20 elif snake_head.direction left: x - 20 elif snake_head.direction right: x 20 snake_head.goto(x, y) def spawn_food(): food.goto(random.randint(-13, 13) * 20, random.randint(-13, 13) * 20) while True: window.update() for i in range(len(snake_body) - 1, 0, -1): snake_body[i].goto(snake_body[i - 1].xcor(), snake_body[i - 1].ycor()) if len(snake_body) 0: snake_body[0].goto(snake_head.xcor(), snake_head.ycor()) move() if snake_head.distance(food) 20: score 1 score_text.clear() score_text.write(f得分: {score}, aligncenter, font(Arial, 16, normal)) new_segment turtle.Turtle() new_segment.speed(0) new_segment.shape(square) new_segment.color(grey) new_segment.penup() if len(snake_body) 0: new_segment.goto(snake_body[-1].xcor(), snake_body[-1].ycor()) else: new_segment.goto(snake_head.xcor(), snake_head.ycor()) snake_body.append(new_segment) spawn_food() if snake_head.xcor() 280 or snake_head.xcor() -280 or snake_head.ycor() 280 or snake_head.ycor() -280: time.sleep(1) snake_head.goto(0, 0) snake_head.direction stop for segment in snake_body: segment.goto(1000, 1000) snake_body.clear() score 0 score_text.clear() score_text.write(f得分: {score}, aligncenter, font(Arial, 16, normal)) for segment in snake_body: if snake_head.distance(segment) 20: time.sleep(1) snake_head.goto(0, 0) snake_head.direction stop for seg in snake_body: seg.goto(1000, 1000) snake_body.clear() score 0 score_text.clear() score_text.write(f得分: {score}, aligncenter, font(Arial, 16, normal)) break time.sleep(delay)运行后窗口会弹出来黑色画布上绿色蛇头停在原点按方向键开始移动。吃到红色圆形食物后蛇身变长撞墙或撞到自己就重新开始。这个版本麻雀虽小五脏俱全贪吃蛇该有的核心元素都有了。3.6 从turtle到pygame后续如何升级跑通之后想继续深入我建议往几个方向走。第一步是加“速度递增”每吃几个食物就把time.sleep的间隔缩短一点难度会明显上来这个改动只需要在得分分支里加一个delay变量控制。第二步是加“最高分记录”把最高分存到本地文件里下次启动读出来显示实战中这就是最简单的数据持久化。第三步是迁移到pygame迁移过程能帮你想明白turtle到底封装了哪些底层逻辑。个人来说我很推荐先用turtle把逻辑全部跑通然后不换逻辑、只换渲染层用pygame重新实现一遍。这样两套库的知识点都能练到你也会发现游戏循环的本质就是“更新状态、渲染画面”的无限循环这个认知对以后做任何图形项目都有用。4. 常见问题与排查技巧实录4.1 方向键按了没反应最可能的两个原因一是执行代码后没调用window.listen()turtle的键盘事件要在这个函数之后才生效二是用了旧接口window.onkey()它绑定的是“按键释放”事件松开按键才触发手感很怪建议统一改成window.onkeypress()。另外窗口必须保持聚焦状态如果焦点跑到别的窗口按键自然收不到。4.2 python --version没输出或提示不是内部命令这不是代码问题是环境变量问题。Windows下重新运行安装包在安装界面确认“Add Python to PATH”已被勾选macOS或Linux有时要输入python3而不是python。改完环境变量后记得重新开一个终端环境变量不会自动刷新到已打开的窗口里。4.3 游戏刚开始就马上结束这种情况十有八九是碰撞阈值写得太宽松比如把判断条件写成了distance 20蛇头刚出生时如果蛇身第一节初始位置离蛇头太近一进主循环就误判为撞到自己。建议把阈值严格设置成小于20并且新增的蛇身节点统一放到蛇尾位置不要放在蛇头附近。4.4 食物生成在蛇身上或者吃掉之后消失完整代码里的spawn_food只是随机挑了一个位置实际跑起来偶尔会生成在蛇身上。概率不高但一旦发生蛇头路过时已经吃不到游戏体验会卡住。改进方法是循环检测新坐标与蛇身的距离直到不重叠再生成。另外随机坐标一定要和20像素对齐否则蛇头从旁边经过时距离永远不恰好小于20会出现“明明碰到了但没得分”的奇怪手感。4.5 画面闪烁、卡顿或者CPU占用过高闪烁是没用tracer(0)导致的初始化里加一行window.tracer(0)主循环里每次update()一次就能解决。CPU占用过高通常是time.sleep(delay)里的delay设得太小比如设成0.01循环跑得太快。贪吃蛇这类游戏delay设在0.08到0.15之间手感最接近经典版本太快的帧率对逻辑简单的游戏没有意义反而增加无谓的CPU开销。跑完这版贪吃蛇接下来就可以试试“碰到食物加速”“加入障碍物”“双人模式一人控制一条蛇”这些方向逻辑都是现成的改起来并不难。游戏写完把代码交给朋友跑一圈看看他们最常死在第几分你就知道下一个该优化什么了。本文还有配套的精品资源点击获取
返回列表