尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Python游戏开发入门:用Pygame从零写一个接苹果小游戏
如果你也在学Python学到列表、函数、循环之后就开始发愁——书上的练习题做完了但“我到底能用Python做什么”。我自己的经验是去做一个能跑起来看的东西。我第一个真正写完的图形程序就是用Pygame写的一个小游戏。这个小程序不复杂一个窗口、几个色块、一个循环跑起来的那一瞬间我才真正理解了什么叫编程——不是背语法而是让代码动起来。这篇文章我会用Pygame带你从零做一个“接苹果”小游戏。它会用到游戏循环、事件处理、键盘控制、碰撞检测、计分和结束条件这几块内容。不管你之前只写过命令行程序还是完全没碰过图形开发只要跟我把这几段代码敲一遍大概率都能跑起来。适合的人群很明确Python入门后想找第一个实战项目的人以及一直搞不懂“游戏循环是什么”的初学者。1. 为什么第一行游戏代码要从Pygame开始1.1 Pygame把游戏开发的门槛降到了哪里游戏开发的难点不在“写代码”而在你离操作系统太近。你想画一个圆先得创建一个窗口和窗口系统打交道你想检测键盘按键得处理底层输入事件。如果从零开始学这些会先被一堆平台相关的概念劝退。Pygame做的事情是帮你把这些底层细节包装成一套简单接口。你不用关心窗口怎么创建、图像怎么渲染、音频怎么混音只需要调用pygame.display.set_mode创建窗口pygame.draw.circle画圆pygame.event.get拿事件。它基于SDL库跨平台表现稳定在Windows、macOS、Linux上都能跑。作为学习工具它的颗粒度刚刚好既没有复杂到你只想放弃也没有简单到什么都替你做了游戏背后的核心概念你仍然需要自己写出来。这里我想强调一个观点初学者不要急着上游戏引擎。引擎确实能让你拖拖拽拽做出东西但你不理解游戏循环和状态更新出了问题就抓瞎。Pygame能让你把“每一帧发生了什么”彻底想明白这部分理解之后换任何引擎都是通用的。1.2 环境准备装对版本比装快更重要装Pygame本身不难真正容易出问题的是环境混乱。我见过一位同学电脑里装了三四个Pythonpip装到了A版本执行时用的却是B版本然后回来问“为什么编辑器里import成功命令行里却ModuleNotFoundError”。推荐的做法是给这个项目单独建一个虚拟环境。命令很简单python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate然后安装Pygamepip install pygame如果下载速度慢把pip默认源换成国内常用镜像源再装。装完后用一行代码确认版本号是否正确python -c import pygame; print(pygame.version.ver)能打印出版本号说明环境OK。如果打印报错先执行python -m pip list看看包有没有真的装上以及是不是装到了当前激活的虚拟环境里。这一步排查顺序比盲目卸载重装高效得多。顺便说一句编辑器IDLE能用但补全和调试太弱了。我现在用的是VS Code这类带智能提示的编辑器写Pygame时补全pygame.display.set_mode这种长函数签名非常省事。编辑器不是关键挑个顺手的就好。1.3 五秒钟看懂你的第一个Pygame窗口装好环境先别急着写游戏我们先写一个最小的窗口用来验证一切是否正常运转import sys import pygame pygame.init() screen pygame.display.set_mode((800, 600)) pygame.display.set_caption(我的第一个Pygame窗口) running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False screen.fill((30, 30, 60)) pygame.display.flip() pygame.quit() sys.exit()这个程序看起来很短四行核心逻辑却是所有Pygame游戏的地基。pygame.init()负责初始化所有模块包括显示、混音、字体等等。pygame.display.set_mode((800, 600))创建了一个宽800高600的窗口返回的screen对象就是你的“画布”。fill((30, 30, 60))把画布填充成深蓝色pygame.display.flip()把画布真正推到屏幕上。很多人第一次写会忘了flip结果窗口一片黑以为程序死了。其实fill只是往内存里的画布上画flip才负责真正显示。可以这么记fill是画画flip是拍照并发朋友圈不发朋友圈别人就看不到。事件循环while running里pygame.event.get()会返回一堆事件对象。你只需要处理pygame.QUIT这是点击窗口关闭按钮时产生的退出事件。用running这个布尔变量控制循环的结束比直接break更干净后面加“游戏结束”状态时也用得上。到这里你的第一个Pygame窗口已经能打开了。接下来我会讲一个比窗口本身更重要的东西游戏循环。2. 先搞懂游戏循环Pygame程序的“心跳机制”2.1 游戏循环到底在循环什么一个游戏程序其实就是一台连续运转的高速机器你看到的每一帧画面都是它在你眼前闪过的无数截图之一。游戏循环存在的意义是让这台机器按固定节奏运转每转一圈游戏世界就更新一次。放到Pygame里循环体里做的事情永远是同样的三件事。第一扫描事件看看玩家按了什么键、点没点关闭按钮第二更新游戏状态把篮子的位置按键盘输入挪一挪把苹果的位置往下移一移第三重新绘制画面把所有更新后的状态画到屏幕上。理解这个循环之后你就会发现很多问题都可以预测如果画面不动通常是绘制没做如果苹果不落是状态更新没写如果窗口无法退出是事件里没处理QUIT。不要急着查网络搜索先问自己我写的这个循环每一圈里三件事做全了吗2.2 事件处理与游戏状态别把两件事混在一起新手最容易掉进去的坑是把“事件”和“状态”混在一起。事件是“一瞬间发生的事情”按键按下、按键松开、鼠标移动、窗口关闭都算事件。比如玩家按下方向键事件循环里收到KEYDOWN事件。但如果你的移动逻辑只写在“按下键那一刻”那玩家按住按键时篮子只会动一格而不是持续移动。状态是“一直存在的事实”篮子的x坐标、苹果的y坐标、当前分数都是状态。我们通常希望按住方向键时篮子的x坐标持续变化这是一种状态变化不应该写在单次事件里。在Pygame里有两种做法。用事件驱动的方式比如判断pygame.KEYDOWN事件再改变一次位置适合单次触发的动作。用状态查询的方式比如key pygame.key.get_pressed()返回当前哪些键被按住适合连续移动。在接苹果游戏里篮子的移动就得用get_pressed。这个细节我会在下一章的代码里展示。术语是“一瞬间发生”还是“持续存在”举例Pygame获取方式事件一瞬间按键按下pygame.event.get()状态持续存在篮子位置、苹果下落速度普通变量/对象属性2.3 帧率、速度和tick之间的算计Pygame提供了一个clock对象用来控制循环速度。clock.tick(60)的意思是让循环每秒钟最多跑60圈也就是60帧。我们通常说“游戏跑60帧”指的就是这个数字。为什么要刻意控制帧率因为如果不控制循环会把CPU吃满游戏在性能强的电脑上飞快、在性能弱的电脑上慢如蜗牛这是不可接受的行为。固定帧率之后大家的行为统一了都是一秒60帧。这时你让篮子每帧向右移动3个像素实际效果就是一秒移动180像素。不过要注意一个隐性陷阱如果电脑性能不够tick(60)达不到60帧每一帧之间的真实时间就会变长。这时如果你还是“每帧移动3像素”游戏速度会变慢。严谨的做法是用时间差delta time来计算移动量。但对第一个小游戏来说先固定tick(60)已经足够。我只是想让你脑子里有个概念速度和帧率是绑定的。3. 完整实现做一个能玩的“接苹果”小游戏3.1 搭建窗口、游戏对象和基础状态这次我们不写复杂素材全部用色块来表示游戏对象。一个绿色方块代表苹果一个灰色长条代表篮子一个深色背景代表游戏区。代码量不大但每一步都有讲究。看一下完整的初始化和对象定义import sys import random import pygame pygame.init() WIDTH, HEIGHT 800, 600 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(接苹果小游戏) APPLE_COLOR (50, 200, 50) BASKET_COLOR (180, 180, 180) BG_COLOR (20, 20, 40) apple pygame.Rect(400, 0, 30, 30) # 苹果x, y, 宽, 高 basket pygame.Rect(370, 540, 80, 15) # 篮子x, y, 宽, 高 speed 3 score 0 font pygame.font.SysFont(None, 36)这里我用的是pygame.Rect矩形来表示对象。Rect在Pygame里是万能的基础数据结构它不仅存了x、y坐标和宽高还提供了move、colliderect这些现成方法。对新手来说先学会用Rect比上手研究精灵系统更合适。定义好Rect之后它们初始位置就在屏幕里排好了。苹果的x设在400正好在窗口水平中间偏左一点y从0开始意思是它会在屏幕顶部出现。篮子的x设在370y设在540也就是接近底部。这个y值需要斟酌篮子太高游戏太简单太低玩家几乎没有反应时间。对800x600的窗口540左右比较舒服。顺手把背景色、前景色、字体对象都定义好。字体后面计分要用这里先创建出来。3.2 键盘控制持续移动而不是“按一下动一下”被按键按住时的持续移动就是我前面说的“状态更新”。这部分代码放在循环内部每一帧执行一次running True while running: for event in pygame.event.get(): if event.type pygame.QUIT: running False keys pygame.key.get_pressed() if keys[pygame.K_LEFT]: basket.x - 6 if keys[pygame.K_RIGHT]: basket.x 6 if basket.left 0: basket.left 0 if basket.right WIDTH: basket.right WIDTH第一段事件循环负责处理退出第二段用get_pressed查询当前哪些按键被按住。注意这里用的是- 6而不是 6因为每一帧循环都可能执行这一行累加就形成了连续移动。每次移动后要做边界限制。比如篮子一直往右不限制的话它会直接“出画布”。边界限制的做法也很直接判断basket.left小于0就把left强制设回0判断right超出窗口宽度就设回WIDTH。这比判断中间坐标更直观因为Rect的left和right可以直接改。写到这里你可能会问为什么不把移动写在事件里事件里也有KEYDOWN吗因为在事件里写只能响应按键按下的那一瞬间。我前面讲过get_pressed返回的是“当前哪些键一直被按住”所以适合连续控制。这是接苹果游戏里一个非常重要的设计选择。3.3 碰撞检测、计分和游戏结束逻辑苹果要往下掉掉的过程中要和篮子碰撞也要检查是否落到底部。接下来是主循环内的第二部分——状态更新和碰撞判定。把下面的代码放在刚才事件处理代码的后面仍然处于while循环内部apple.y speed if apple.colliderect(basket): score 1 apple.y 0 apple.x random.randint(0, WIDTH - 30) if apple.bottom HEIGHT: running False第一行让苹果每帧向下移动speed个像素。这里注意坐标系方向屏幕左上角是(0,0)x向右变大y向下变大。所以苹果“下落”其实是y的数值一直在增大不是减小。这个方向感新手很容易弄反。然后判断苹果和篮子是否发生碰撞。pygame.Rect提供了现成的colliderect方法两个矩形只要相交就返回True。碰撞后分数加1苹果回到屏幕顶部并且x坐标用random.randint(0, WIDTH - 30)重新生成让下一轮苹果不从同一个位置掉下来。最后如果苹果的bottom大于窗口高度说明苹果已经掉出场外游戏结束。此时把running置为False循环退出。为了让玩家看到最终分数在循环退出前先把分数绘制到屏幕上再短暂等待一下。绘制部分的代码screen.fill(BG_COLOR) pygame.draw.rect(screen, APPLE_COLOR, apple) pygame.draw.rect(screen, BASKET_COLOR, basket) score_text font.render(得分: str(score), True, (255, 255, 255)) screen.blit(score_text, (10, 10)) pygame.display.flip()fill清空画布draw.rect把矩形画到画布上font.render把文字渲染成图片然后用blit把图片贴到左上角。最后flip更新屏幕。注意填色和绘制的顺序先填充背景再画对象否则背景会把对象盖掉。到这里把这几段代码按顺序拼到一个.py文件里就是一份完整可运行的接苹果游戏。核心逻辑只有几十行但这个结构已经包含了几乎所有2D小游戏的骨架。4. 开发中容易踩的坑事件重复、穿透与速度漂移4.1 碰撞计分为什么会“重复加分”我最初写接苹果时碰到的第一个诡异Bug苹果明明只碰了一次篮子分数却一下涨了四五分。原因很简单苹果进入篮子矩形之后并没有立刻离开。如果游戏循环在那一瞬间已经跑了好几帧而每一帧的碰撞检测都成立就会重复加分。换句话说苹果躺在篮子的“怀里”期间碰撞条件一直为真。解决办法有两种。第一种碰撞之后立刻把苹果重置回顶部并且保证重置后的位置不再与篮子重叠这样下一帧的碰撞检测就自然为假了。第二种给苹果加一个“已经计过分”的标记只有第一次碰撞时加分等苹果离开篮子后再清除标记。对简单游戏来说第一种最省事也是我推荐的。把这个经验记下来碰撞一方重置位置是避免重复计分最自然的方案。如果你做的是多个物体碰撞同一个目标的游戏重置或标记两种思路要灵活组合。4.2 高速移动时如何避免穿透判定第二个坑紧跟着出现如果苹果速度太快它会直接“穿过”篮子根本没有碰撞发生。矩形碰撞是基于“相交”判断的。如果苹果一帧之前还在篮子左边下一帧移动了很远直接到篮子右边两个矩形在这一帧的交集可能为空碰撞检测就漏了。这在游戏中叫隧穿效应。新手阶段最简单的解决方案是限制最大速度。在接苹果游戏里我建议把苹果下落速度控制在每秒不超过窗口高度的一半。如果你确实需要很高的速度就得用更进阶的手段比如把物体在上一帧和当前帧之间的路径也当成一个矩形来做检测或者采用更小的步长分多次移动。第一次做游戏不用走这么远把速度控制在合理范围就够了。顺带一提这也解释了为什么某些游戏的“高速下落”模式里玩家经常会遇到“明明挡住了却还是穿过去”的体验——游戏开发者没有处理高速碰撞或者有意识地用其他机制掩盖了它。现象原因处理分数一次加好几碰撞条件持续成立重置物体位置或添加标记苹果穿过篮子高速移动导致矩形不相交限制速度/步长细分游戏在不同电脑速度不同帧率不稳定用时间差计算移动量4.3 帧率不稳定如何让游戏变得“忽快忽慢”第三个坑发生在不同电脑上。你在自己的机器上觉得苹果下落速度很舒服拿到另一台配置较差的电脑上明显变慢了。原因不在代码逻辑而在帧率。如果电脑跑不满60帧clock.tick(60)实际上会让循环每秒只跑40帧。这时苹果每帧移动speed个像素每秒的总移动量就变小了游戏自然变慢。要解决这个问题就要脱离“帧”的思维改用“秒”的思维。先定义一个速度单位是“像素/秒”然后在每一帧里用这一帧的真实时间差去计算移动量。具体做法是记录上一帧的时间用pygame.time.get_ticks()或time.time()算出两帧之间的毫秒差再乘以速度。这样做的好处是帧率再不稳定移动速度都能保持一致。初学者可以先不实现这种delta time机制但至少要理解这个思路。等把第一个游戏跑通了我再建议你把速度参数从“每帧3像素”改成“每秒180像素delta time”你会对游戏循环的理解上一个台阶。5. 让游戏更有质感音效、中文显示与扩展方向5.1 加一点声音反馈游戏立刻不一样一个没有声音的游戏就像哑巴在说话功能完整但少了味道。Pygame里加音效非常简单前提是你得有一个音频文件。pygame.mixer.init() catch_sound pygame.mixer.Sound(catch.wav)然后在碰撞代码里加一句if apple.colliderect(basket): catch_sound.play()这里有几个注意点。一个是音频格式Pygame的默认支持里wav和ogg是最稳妥的选择mp3在某些环境解码可能有问题。另一个是文件路径如果你把游戏打包给别人音频文件路径千万不能写死成绝对路径最好用脚本所在目录作为基准。没有音频文件怎么办网上找免费的短音效也可以用系统自带的一些工具生成一个简单的“叮”声。再不行先用一个长短不同的延迟提示代替也没问题。游戏体验的优先级里反馈及时比反馈精美更重要先保证每次碰撞都有一个提示。5.2 中文显示的小坑与字体加载方案接苹果游戏的计分如果用中文“得分”显示很多新手会发现文字渲染出来是空白的方块或者干脆不显示。原因是pygame.font.SysFont(None, 36)会走系统默认字体而默认字体不一定会帮你找到可用的中文字体。解决方法是先手动找一个中文字体文件用pygame.font.Font(font_path, size)加载它。代码大概是这样font pygame.font.Font(path/to/your/chinese_font.ttf, 36)更通用的做法是让代码自动查找系统中可用的中文字体。可以先调用pygame.font.get_fonts()拿到所有可用字体列表再在里面搜索包含“notosanscjk”“wqy”“微软雅黑”等关键词的字体。如果找不到就退回用英文显示。这套逻辑大约十行但能让你避免在别人电脑上打开游戏发现一堆方块的尴尬。字体这一块是比较典型的环境差异问题。它和开局的环境安装问题一样都属于“你机器上是好的换台机器就坏了”的范畴。做任何需要分发的小程序时都要提醒自己代码不依赖本机特殊资源才可移植。5.3 如果你想把“小游戏”做成“作品”到这里一个能玩、有声、有分的接苹果小游戏就算完成了。但如果你不满足于“能跑”我建议你从这个版本开始做三件升级。第一把单个苹果改成苹果列表用数组管理多个苹果同时下落。这一步会强迫你从“写死一个对象”转向“批量管理对象”是通往精灵系统的第一步。第二加入难度递增每得5分就把speed变量加1玩家会明显感觉到游戏在逼近自己的反应极限耐玩度立刻提升。第三把最高分保存到本地文件下次启动游戏时读取并显示。这是最简单的“存档”雏形也是做更复杂游戏持久化的起点。如果你真的想把这个小游戏做成可以拿得出手的Demo我的建议是先不要急着加功能。把你现有代码里的每一行都看懂、改一遍故意弄坏再修复。这一步比多写三个功能都值钱。等你能随手把拖动篮子的速度从“每帧3像素”改成“每秒180像素”时你对Pygame的理解就已经超过大多数只跟着教程抄代码的人了。
RELATED

相关推荐

使用Jmeter压测Kafka:指标分析、脚本实现与性能调优实践

使用Jmeter压测Kafka:指标分析、脚本实现与性能调优实践

1. 压测目标与方案选型:为什么用Jmeter测Kafka1.1 Kafka性能验证的核心指标Kafka这类消息中间件,性能测试的重点和普通HTTP接口不太一样。HTTP接口压测关注的是QPS、响应时间和错误率,而Kafka压测要看的是一套组合指标:写入吞吐量…

📅 2026/10/20 2:44:18
AI Agent 浏览器自动化工具:agent-browser 的 Chromium CLI 配置与验证

AI Agent 浏览器自动化工具:agent-browser 的 Chromium CLI 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/10/20 2:44:18
Coze智能体工作流:1分钟生成修仙短视频的完整指南

Coze智能体工作流:1分钟生成修仙短视频的完整指南

2. 关键词魔法:从文案到画面的一个映射看到这里,你可能已经意识到一个关键问题:工作流的核心,就是如何把“玄幻修仙”这四个字,翻译成一连串画面。我实测下来,Coze智能体工作流里最常用、也最稳的画面实现方…

📅 2026/10/20 2:44:18
MORE NEWS

更多资讯

📰

GET与POST发送普通参数全解析:编码、Content-Type与联调避坑

做开发这几年,我发现自己反复在跟一些"简单到不可能出错"的东西搏斗。GET和POST发送普通参数就是典型——不就是往URL后面拼几个keyvalue,或者往body里塞一段表单嘛,能有多难?可真到了前后端联调的时候,各种…

📰

AI写学术专著靠谱吗?从工具横评到高效流程的完整攻略

很多人常问,AI都这么能写了,是不是丢进去一个想法,就能自动吐出一本几十万字的学术专著?答案会让你失望。基于我长期跨领域的信息工程与知识管理经验,我得先泼一盆冷水:AI写短文可以“一键秒杀”&#xff0…

📰

Python65:核心语法-函数进阶-默认参数

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

Excel/WPS 里像写公式一样查 A 股行情:free-stockdb STOCK_DATA 宏快速上手指南

Excel/WPS 里像写公式一样查 A 股行情:free-stockdb STOCK_DATA 宏快速上手指南 【免费下载链接】free-stockdb 面向 A 股日K、分钟K与ETF分钟数据的本地量化引擎,集成增量同步、本地缓存、复权、批量查询、回测与指标计算。 项目地址: https://gitcod…

📰

DeepSeek-V3-0324 本地加载实战:从 685B 权重到推理跑通

DeepSeek-V3-0324 本地加载实战:从 685B 权重到推理跑通 【免费下载链接】DeepSeek-V3-0324 DeepSeek最新推出DeepSeek-V3-0324版本,参数量从6710亿增加到6850亿,在数学推理、代码生成能力以及长上下文理解能力方面直线飙升。 项目地址: ht…

📰

WebSocket群聊架构实战:握手鉴权、消息广播与断线重连

做群聊功能之前,我一直觉得WebSocket不过就是"前端new一个socket,后端onMessage收一下再广播"的事。直到有一次压测时,模拟200个客户端同时进房间刷消息,服务端CPU直接飙到90%,还有一批消息悄无声息地消失在…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬