ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

从零用Pygame开发Python小游戏:完整流程与实战避坑指南

从零用Pygame开发Python小游戏:完整流程与实战避坑指南 Pygame 是我在 Python 里第一个真正拿来做“能玩的东西”的库。当年我用它写完第一款小游戏时那种“原来代码真的可以把画面、音效、按键反馈捏在一起变成游戏”的感觉到现在都还记得。如果你刚学完 Python 基础正憋着一股劲儿想写点能跑、能玩、能发给朋友折腾的东西用 Pygame 做一个小游戏几乎是最顺的一条路——它不需要你掌握多少图形学知识也不需要下载几个 G 的游戏引擎一套 Python 环境加一个 pip 命令就能开工。这篇文章我会从零开始把用 Pygame 开发小游戏的全过程捋一遍为什么选它而不选别的方案、环境怎么装、最小窗口骨架怎么写、坐标系和游戏对象怎么理解、如何一步步做一个完整的躲避类小游戏、最后怎么打包成 exe 发给别人玩以及我踩过的一些坑和优化经验。无论你是刚入门的 Python 新手还是想快速产出一个小项目交作业、参加 Game Jam、或者纯粹想体验一把“作者视角”都应该能从这里拿走可以直接用的东西。1. 为什么用Pygame做第一个小游戏很多人一提到做游戏第一反应是 Unity、Godot 这些重量级引擎。它们确实强大但对刚学完 Python、还没写过几千行代码的人来说学习曲线太陡了。Pygame 恰好卡在了一个很舒服的位置它不是一个完整的游戏编辑器而是一组 Python 的 2D 游戏开发库负责帮你处理窗口创建、图像绘制、声音播放、键盘鼠标事件这些最底层的事情而你只需要用 Python 代码去描述“游戏世界长什么样、规则是什么”。1.1 Pygame能做什么、到底适合谁Pygame 的核心能力其实就几块创建窗口并实时刷新画面加载和绘制图片、矩形、圆形、文字等基本图形播放音效和背景音乐监听键盘、鼠标事件还提供了一套精灵Sprite系统方便管理一群会移动、会碰撞的游戏对象。这些能力覆盖了大部分 2D 小游戏的需求贪吃蛇、打砖块、飞机大战、俄罗斯方块、躲避障碍、横版过关甚至简单的双人对战都没问题。它适合谁首先是刚学完 Python 语法、想找个真实项目练手的人。其次是做毕业设计或课程设计的学生用 Pygame 写一个带交互、带图形界面的小游戏比写命令行程序能展示的东西多得多。还有一些想快速做游戏原型验证点子的独立开发者——用 Pygame 写原型上手快改起来也快等玩法确认了再移植到其他引擎也不亏。它不适合谁如果你目标是开发大型商业游戏、3D 游戏或者面向微信小游戏、抖音小游戏这类平台那就不要选 Pygame。它不是为这些场景设计的移动端支持也基本等于零。这一点我后面会专门展开说免得你走弯路。1.2 和其他方案怎么选Python 生态里做 2D 游戏除了 Pygame还有几个常见选择Pygame Zero、Arcade、Ursina再加上更外层的 Godot、Unity。很多人一开始会纠结选哪个我的建议很直接第一选择 Pygame。原因很简单资料多、社区大、遇到问题搜得到答案这对新手来说比框架本身好不好用更重要。方案上手难度可视化程度主要用途坑点Pygame中等纯代码2D小游戏、教学项目需要自己处理主循环和事件Pygame Zero低纯代码做了封装少儿编程、快速原型功能被阉割自定义性差Arcade低中纯代码2D教学、小游戏生态比 Pygame 小Ursina低中纯代码部分简化快速3D原型3D深度有限Godot高编辑器代码2D/3D完整游戏学习成本高有编辑器负担Unity高编辑器代码商业游戏、小游戏移植重Python基础用不上Pygame Zero 是在 Pygame 之上包了一层教学友好的封装对孩子学编程确实比 Pygame 直接但很多东西被“省掉”了你就少了一个理解底层的窗口。Arcade 也很好但社区和教程数量跟 Pygame 相比差一个量级。如果你只是写作业或者第一个游戏我真找不到理由不选 Pygame。1.3 选型背后的逻辑我特别想强调一个观点用 Pygame 最大的价值不是做出一个多精致的游戏而是你被迫去理解“游戏是怎么跑起来的”。在 Unity 里拖一个方块到场景里拖动一个脚本组件几秒钟就能跑出东西但你可能完全不理解背后的事件循环、渲染刷新、碰撞检测是怎么发生的。Pygame 逼你自己写主循环自己处理事件队列自己判断两个矩形是否相交。这个过程哪怕很简陋它带给你的心智模型是扎实的。这种底层理解的价值会在你将来学任何图形库、游戏引擎甚至是做 GUI 程序的时候体现出来。所以选 Pygame 并不是因为它比别的方案“高级”而是因为它“透明”——你写的每一行代码都直接对应到游戏运行的一个真实环节没有黑盒帮你隐藏。2. 环境准备与项目初始化标题里的第一件正事就是装环境。Python 用户应该都知道一句话能用 pip 解决的问题都不叫问题。Pygame 的安装就这样常规情况下一条命令搞定但这条命令前后的一些小细节新手经常栽跟头我在这里讲透。2.1 安装Pygame从pip开始先确认你电脑上有 Python 3.8 或更高版本。在命令行里执行python --version或者python3 --version能输出版本号就行。然后安装pip install pygame如果你用的是 Python 3 和 pip 分开命名的环境那可能要用pip3 install pygame国内网络下载慢的话可以直接指定清华镜像源速度快很多pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple装完后最好验证一下不要在没有任何验证的情况下直接开写代码。运行python -c import pygame; print(pygame.version.ver)正常情况下会输出类似2.6.1的版本号。这一步如果报No module named pygame先检查你是不是用了多个 Python 环境——最常见的问题就是你同时装了系统 Python 和 Anacondapip 装到了一个环境里运行时却用另一个环境。碰到这种情况统一用py -3 -m pip install pygame这类带解释器标识的命令安装能少折腾很多。还有一个建议给这个项目建一个虚拟环境。后续你可能要给游戏装 PyInstaller、numpy 之类的东西虚拟环境能保证这些依赖不污染全局 Python。建立和使用命令很简单python -m venv gameenv gameenv\Scripts\activate # Windows source gameenv/bin/activate # Linux/macOS2.2 第一段能跑起来的窗口代码环境弄好之后我强烈建议你先跑通下面这个“最小窗口骨架”不要急着写游戏逻辑。这个骨架虽然只有二十多行但它包含了所有 Pygame 程序都跳不出去的框架初始化、创建窗口、进入主循环、处理事件、刷新画面。你后面写的每一个游戏都是在这个骨架里长出来的。import pygame pygame.init() # 初始化所有模块 WIDTH, HEIGHT 800, 600 screen pygame.display.set_mode((WIDTH, HEIGHT)) # 创建窗口 pygame.display.set_caption(我的第一个Pygame游戏) clock pygame.time.Clock() # 游戏时钟控制帧率 running True while running: for event in pygame.event.get(): # 取出所有事件 if event.type pygame.QUIT: # 点击窗口关闭按钮 running False screen.fill((20, 20, 20)) # 用深灰色填充背景 pygame.display.flip() # 把画好的内容显示到窗口 clock.tick(60) # 限制帧率在60FPS pygame.quit()逐行看几个关键点。pygame.init()我建议你无脑调用它会一次性初始化所有需要初始化的模块包括视频、音频、字体等。如果你省略它很可能在用到文字或者音效时突然报错排查半天才发现是初始化问题。pygame.display.set_mode((WIDTH, HEIGHT))是创建窗口的唯一入口参数是一个元组表示窗口大小。它返回一个 Surface 对象你可以把它理解成一块画布所有绘制都是在这块画布上进行的。while running:这个循环是整个游戏的命脉游戏里每一帧要做的事情都写在循环体里读取玩家输入、更新所有对象位置、检测碰撞、绘制画面。它每秒钟会执行大约 60 次这就是你屏幕上“游戏画面”形成的原理——所谓的动画其实就是快速切换一帧帧细微变化的静止画面。这个主循环概念比任何语法都要重要我后面会反复提到它。pygame.display.flip()的作用是把画布上已经绘制好的内容真正“翻”到窗口上。如果你不调用它屏幕上会一直是黑的。Pygame 之所以要分两步是为了避免你绘制一半时画面被肉眼看到导致闪烁和残影。跑完这个最小骨架后你可以继续在里面画一个矩形试试比如在填充背景后加一行pygame.draw.rect(screen, (255, 255, 255), (100, 100, 50, 50))然后再运行看看。这一步能确认绘制链路是通的为后面的正式开发打基础。3. 核心细节解析与实操要点写完最小骨架之后就轮到理解几个“绕不开”的概念了。这些概念如果你没搞懂后面写游戏会处处碰壁搞懂了你会发现 Pygame 其实非常简单。3.1 坐标系、颜色和绘制基础Pygame 的坐标系跟你在数学课上学过的直角坐标系不一样它的原点(0, 0)在窗口的左上角x 轴向右增加y 轴向下增加。也就是说一个点的 y 值越大它在屏幕上越靠下。这一点初学时要特别留意尤其是你想让物体“向下”移动时代码写的是让 y 数字增大而不是减小。颜色方面Pygame 使用的是 RGB 三通道每个值在 0 到 255 之间。比如(255, 0, 0)是纯红色(0, 255, 0)是纯绿色(255, 255, 255)是白色。建议你在代码里一开始就定义好颜色常量不要满代码写裸的数字不然隔两天再看代码谁也不知道那个(200, 30, 30)是什么东西。绘制基础其实就几个常用函数pygame.draw.rect()画矩形pygame.draw.circle()画圆形pygame.draw.line()画直线pygame.draw.polygon()画多边形。它们都有一个共同特点第一个参数是目标 Surface通常是主窗口screen最后一个参数是线的宽度填 0 表示实心填充。这些函数可以直接用来画简单的游戏角色和障碍物完全不需要准备美术素材。3.2 Rect游戏里的隐形万能胶Pygame 里最实用、但新手最容易忽略的一个数据结构就是pygame.Rect。它本质上是一个矩形对象里边存着x、y、width、height四个核心属性同时提供了一整套便捷的方式表示位置和大小。player pygame.Rect(100, 100, 50, 50)这行代码创建了一个位于(100, 100)、宽高都是 50 的矩形。你可以通过player.x、player.y直接修改位置通过player.center直接拿到中心点坐标通过player.left、player.right、player.top、player.bottom快速访问四条边。我在项目里经常用player.center (x, y)这种写法因为它无论怎么设置中心点都会自动对准目标坐标省去了手动计算左上角位置的麻烦。Rect 更大的价值在于碰撞检测。Pygame 的 Rect 自带一个colliderect()方法用来判断两个矩形是否相交if player.colliderect(ball): game_over True这一行就完成了游戏中最常见的“撞到东西了”的判断。新手自己写碰撞逻辑往往会陷入“物体中心距离小于多少”这种繁琐计算但用 Rect 都是现成的。背后大概的原理并不复杂就是比较两个矩形的四条边是否交错但对于一个小游戏来说你根本不关心底层怎么算好用才是第一位的。有一点要注意Rect 内部存的是整数所以当你打算用浮点数记录精细位移比如每帧移动 0.5 像素时不要直接把浮点数赋给rect.x会丢精度。正确做法是用一个浮点变量记录位置每帧把计算结果取整后再赋给 Rect保证视觉定位和碰撞检测都不出错。3.3 事件与键盘控制玩家指令怎么进来游戏不能只自己动还需要和玩家交互。Pygame 里所有交互都基于“事件系统”。窗口被关闭、键盘被按下、鼠标被移动Pygame 都会把这些东西塞进一个事件队列你需要在主循环里用pygame.event.get()把它们取出来处理。这里有两个容易混的概念一个是“按下事件”一个是“持续按住状态”。我的经验是做动作类游戏移动角色用持续按住状态做菜单选择或“按下才触发”的操作比如按下空格跳跃、按下 R 重开用事件两者要区分对待。持续按住的代码模式是keys pygame.key.get_pressed() if keys[pygame.K_LEFT]: player.x - speed if keys[pygame.K_RIGHT]: player.x speedpygame.key.get_pressed()返回所有按键的实时状态True 表示当前这个按键按着。在这段代码里你不需要去一个个处理 KEYDOWN 事件只需要每帧读取状态就能获得平滑的持续移动效果。而处理单次触发的事件模式是这样的for event in pygame.event.get(): if event.type pygame.KEYDOWN: if event.key pygame.K_SPACE: player.jump()注意一个关键点主循环里必须每帧调用一次pygame.event.get()把事件队列清空。如果你只调用get_pressed()而不调用event.get()或者调用得太少窗口就会变得“无响应”——因为它积压的事件一直没有被处理系统觉得你的程序卡死了。这个坑极其常见我见过好几个新手写的程序窗口能显示但一点关闭按钮就没反应根因就在这里。3.4 一个好设计信号Game Loop讲到这里我忍不住想多啰嗦一点主循环的价值。Pygame 的主循环不是随便写的它是所有实时游戏程序的通用骨架。它本质上就是“每帧重复做三件事处理输入、更新状态、绘制画面”。如果你能把这个 Loop 记在心里之后学 Unity 的 Update、Godot 的_process()你会发现它们做的事情一模一样只是名字和写法不同。很多新手喜欢在循环里写大量逻辑把整个游戏塞进一个while True里这也能跑但代码很快就乱成一团。我给的建议是从一开始就按主循环的职责拆分代码块事件处理写一块世界更新写一块绘制写一块中间用注释隔开。哪怕你现在只有三五十行代码这个习惯也能让你后面加功能时不至于无从下手。4. 一步步做出一个完整小游戏理论说了一堆现在来实操。我选了一个非常适合入门的游戏类型躲避掉落物。玩家控制一个角色左右移动躲避从屏幕顶部不断落下的雪球每躲过一个就得分被砸到就游戏结束。这个游戏足够简单但涵盖了 Pygame 开发中的绝大多数核心环节对象生成、位置更新、碰撞检测、计分、游戏状态切换。做完它你就已经有能力自己改造成飞机大战、接水果、打地鼠等各种变体了。4.1 先定游戏规则写代码之前先把规则和数据想清楚。这一步千万别省想清楚再写和你边写边猜的效率差别非常大。我设计的规则是窗口大小 800x600玩家是一个蓝色方块位置在底部中间用左右方向键控制移动。雪球是红色圆形从窗口顶部随机水平位置出现匀速向下落。雪球落出屏幕底部就算“安全躲过”得一分。玩家与雪球发生碰撞游戏结束按 R 键重新开始。涉及到几个关键数据玩家尺寸建议 50x50移动速度我初定每帧 8 像素这个值对应 60FPS 下每秒 480 像素大概够快又不会太难控制雪球尺寸直径 30 左右下落速度每帧 6 像素以后可以随分数提升。4.2 从零写代码分步第一步先初始化并规划全局常量。统一放到文件顶部的好处是后面你想调难度、换窗口大小只需要改这一处不用满文件找数字。import pygame import random pygame.init() WIDTH, HEIGHT 800, 600 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(躲避雪球) clock pygame.time.Clock() BLUE (80, 140, 255) RED (255, 80, 80) WHITE (255, 255, 255) BLACK (20, 20, 20) PLAYER_W, PLAYER_H 50, 50 PLAYER_SPEED 8 BALL_SIZE 30 BALL_SPEED 6第二步创建玩家对象和用于存放雪球的列表。我用pygame.Rect作为玩家的“碰撞体”同时也用它定位渲染位置。雪球则用一个列表动态管理因为数量是不固定的。player pygame.Rect(WIDTH // 2 - PLAYER_W // 2, HEIGHT - 80, PLAYER_W, PLAYER_H) balls [] # 存放所有雪球 Rect score 0 game_over False第三步定义“创建雪球”的函数。为了让雪球出现位置随机我使用random.randint(0, WIDTH - BALL_SIZE)生成横坐标y 从顶部上方开始这样它一进入画面就是完整的。def create_ball(): x random.randint(0, WIDTH - BALL_SIZE) y -BALL_SIZE balls.append(pygame.Rect(x, y, BALL_SIZE, BALL_SIZE))第四步处理主循环里的各类逻辑。事件部分需要监听退出事件和 R 键重置更新部分负责读取键盘移动玩家、生成新雪球、刷新所有雪球位置、删除越界的雪球、判断碰撞绘制部分负责画背景、画玩家、画雪球、画分数和结束文字。这里有个关键细节我单独拿出来讲在遍历balls列表删除元素时不要直接用for ball in balls:再remove。因为删除前面元素会导致后面的索引错位容易出现“漏删、跳过”的奇怪 bug。稳妥的做法是遍历balls[:]也就是列表的副本在里面做删除判断这样原列表怎么变都不会影响你的遍历过程。这种小习惯写游戏时会用到非常多次。雪球生成频率也要说一下。我用了if random.randint(0, 20) 0:来控制生成概率。也就是说每帧有 1/20 的概率生成一个新雪球在 60FPS 下平均每秒会生成约 3 个。这个频率用于前期难度是合适的。你可以通过调大这个分母来降低难度调小分母来增难。循环里我使用clock.tick(60)锁定帧率为 60。这个很重要如果你的电脑很快而你不限制帧率动画会快到没法玩如果电脑慢又会卡顿。限制帧率能让游戏速度在不同电脑上保持一致。4.3 完整代码整合把上面这些碎片拼起来就是一份可以完整运行的游戏代码。我建议你新建一个main.py把下面的代码直接放进去跑通后再逐步修改边改边理解每一段的作用。import pygame import random pygame.init() WIDTH, HEIGHT 800, 600 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(躲避雪球) clock pygame.time.Clock() BLUE (80, 140, 255) RED (255, 80, 80) WHITE (255, 255, 255) BLACK (20, 20, 20) PLAYER_W, PLAYER_H 50, 50 PLAYER_SPEED 8 BALL_SIZE 30 BALL_SPEED 6 player pygame.Rect(WIDTH // 2 - PLAYER_W // 2, HEIGHT - 80, PLAYER_W, PLAYER_H) balls [] score 0 game_over False font pygame.font.SysFont(microsoftyahei, 36) def create_ball(): x random.randint(0, WIDTH - BALL_SIZE) y -BALL_SIZE balls.append(pygame.Rect(x, y, BALL_SIZE, BALL_SIZE)) running True while running: screen.fill(BLACK) for event in pygame.event.get(): if event.type pygame.QUIT: running False if event.type pygame.KEYDOWN and game_over: if event.key pygame.K_r: balls.clear() score 0 game_over False player.x WIDTH // 2 - PLAYER_W // 2 if not game_over: keys pygame.key.get_pressed() if keys[pygame.K_LEFT]: player.x - PLAYER_SPEED if keys[pygame.K_RIGHT]: player.x PLAYER_SPEED player.x max(0, min(WIDTH - PLAYER_W, player.x)) if random.randint(0, 20) 0: create_ball() for ball in balls[:]: ball.y BALL_SPEED if ball.top HEIGHT: balls.remove(ball) score 1 for ball in balls[:]: if player.colliderect(ball): game_over True pygame.draw.rect(screen, BLUE, player) for ball in balls: pygame.draw.circle(screen, RED, ball.center, ball.width // 2) score_text font.render(分数: str(score), True, WHITE) screen.blit(score_text, (10, 10)) if game_over: over_text font.render(游戏结束! 按 R 重新开始, True, WHITE) screen.blit(over_text, (WIDTH // 2 - over_text.get_width() // 2, HEIGHT // 2 - over_text.get_height() // 2)) pygame.display.flip() clock.tick(60) pygame.quit()还记得我前面讲的“Rect 存整数”的坑吗这段代码里所有移动都是整数像素每帧所以直接用ball.y BALL_SPEED没问题。如果你是做更精细的移动记住那个用浮点记录位置的方案。4.4 难一点与好看一点这个基础版你做出来之后可以立刻加上难度递增玩法会立刻有趣很多。最简单的做法是用分数控制下落速度speed BALL_SPEED score // 10每得 10 分下落速度加 1。这样游戏会有一个从休闲到紧张的自然节奏玩家的心跳会跟着分数往上走。好看一点的包装也不难。雪球除了画成圆形你还可以叠加一层高光——在圆的左上角偏一点的位置画一个更小、更浅的圆看起来会有立体感多一点。玩家方块也可以画“眼睛”其实就是两个小白点加两个小黑点瞬间从方块变成雪人。这种小细节对游戏感受的提升远比你想象的大。还有一个很影响体验的点文字显示。我用的是pygame.font.SysFont(microsoftyahei, 36)在 Windows 上一般没问题但换到 macOS 或 Linux这种中文字体名可能不存在字就会变成方框。稳妥的做法是去网上下一个 .ttf 字体文件放进项目目录然后用pygame.font.Font(font.ttf, 36)加载。这样打包给别人时也不依赖对方系统有无对应字体。5. 打包发布与进阶方向游戏做出来之后第二件让人兴奋的事就是“发给别人玩”。这里我展开聊聊打包成 exe 的完整流程以及很多人关心的“能不能上架小游戏平台”的实际情况。5.1 PyInstaller 打包成exe把 Python 程序打包成 exe目前最成熟的工具是 PyInstaller。安装很简单pip install pyinstaller打包的基本命令pyinstaller -F -w main.py-F表示打包成单个 exe 文件方便分发-w表示运行时不弹出黑色控制台窗口。如果你的游戏里用到了图片、字体、音效这类外部资源文件直接 -F 打包会有个经典的坑PyInstaller 默认会把资源文件放在一个临时解压目录里而你代码里写的相对路径是找不到那个目录的。解决办法是在代码里加一段判断import sys import os def resource_path(relative): if hasattr(sys, _MEIPASS): return os.path.join(sys._MEIPASS, relative) return os.path.join(os.path.abspath(.), relative)所有加载资源的地方都改成用resource_path(font.ttf)这样开发时和打包后都能正确找到文件。这个坑是我第一次打包时踩的当时明明测试一切正常发给朋友却说找不到文件排查了半天才发现是这个路径问题。最后提醒一句PyInstaller 打出来的 exe 经常会被 360、Windows Defender 等杀毒软件误报。这不算 PyInstaller 的错开源工具打包的程序被误报是常见现象你不用太紧张。介意的话可以考虑了解 Nuitka 编译方案或者直接上传到 itch.io 这类游戏分享平台给个浏览器运行版本。5.2 Web和小程序Pygame的边界在哪我看到网上很多人问“用 Pygame 做的游戏能不能当微信小游戏、抖音小游戏上架”这里我把话说清楚不能而且非常不建议在这个方向上花时间。Pygame 依赖 Python 运行时和原生窗口微信小游戏、抖音小游戏用的是 JavaScript/WASM 或者专门的引擎导出模式两者根本不是一个运行环境。如果你铁了心要把这个小游戏发到这些平台上正确的路径是用 Unity、Cocos 或者小游戏原生框架重写一遍。而且这些平台对游戏上架有一套自己的审核规则很多细节在开发前就需要了解比如现在微信小游戏要求必须接入侧边栏复访能力没有这个能力的版本直接就会被审核后台拒绝提交多少次都过不了。平时刷到的“网页小游戏”“HTML5 小游戏”很多都是 HTMLJS 写的单文件浏览器打开就能玩。那也是一条独立的技术路线跟 Pygame 互补。我的建议是如果目的是学习就把 Pygame 这条桌面路线走完如果目的是上架平台一开始就换技术栈两条路别混着走。5.3 后续能怎么扩展这个躲避雪球的小游戏后续的扩展空间其实非常大。你可以加一个简单的排行榜把最高分存到文件里下次启动还能读到可以加暂停功能按 P 键暂停按任意键继续可以加背景音乐和音效pygame.mixer模块加载一个 mp3 或 wav 文件就能循环播放甚至可以加双人对战模式第二个玩家用 AD 键控制另一个角色比比谁最后被砸到。还有一个我很推荐的扩展方向是粒子效果和屏幕震动。物体被摧毁时撒一堆粒子角色被击中时屏幕抖一下这些效果用 Pygame 都能实现而且代码量不大。它们对游戏“手感”的提升非常明显属于花小钱办大事的类型。6. 常见问题与排查技巧实录最后这部分我把平时帮别人调试时最常遇到的问题整理一下。这些坑我或多或少都踩过你提前知道了能少走很多弯路。6.1 新手最容易踩的5个坑现象根因解决办法窗口点了关闭没反应主循环没调用pygame.event.get()事件积压每帧循环开头处理事件队列程序一运行就闪退没有初始化模块或图片路径不对检查pygame.init()检查资源文件路径中文字显示成方块系统没有对应中文字体用pygame.font.Font加载 ttf 字体文件游戏有时快有时慢没有clock.tick(60)固定帧率主循环末尾统一控制帧率双击 exe 没反应打包时不带资源文件或报错信息一闪而过用resource_path()处理路径先用命令行运行排查闪退这个问题我想单独展开一下。用 PyCharm 或者 VS Code 运行时能看到报错没事但打包成 exe 或者双击 .py 文件运行时任何异常都会导致一个黑色窗口一闪而过你根本来不及看清错误。这时候最快的调试方法是打开命令行窗口在命令行里python main.py让报错信息留在屏幕上。我每次处理“闪退”类问题时基本都是靠这个一看一个准。事件积压导致的窗口无响应值得你特意去体会一下把pygame.event.get()那行代码注释掉再运行程序你会看到窗口能显示但怎么点都没反应。记住这个现象以后遇到类似的卡死问题第一个怀疑对象就是事件循环没处理好。6.2 性能与手感优化心得小游戏刚做出来时可能你感觉有点“飘”或者“粘”这通常是两个原因帧率没锁导致角色移动速度看着不对或者移动速度参数调得不合适。解决方法是先把帧率锁到 60然后在固定的帧率下调整PLAYER_SPEED。6 到 10 这个范围对于 800 宽的窗口来说都算合理你试到舒服为止就行。如果游戏后期有一大堆雪球同时存在性能下降先检查是不是每帧都做了大量不必要的绘制。Pygame 的绘制本身不是免费的矩形和圆形很多时可以先只刷新需要变动的区域用pygame.display.update(rects)而不是全局flip()。不过对一个新手项目来说这种优化通常不急等你真的遇到性能问题了再上。优化手感还有一个独门心得玩家控制响应要“立刻”一点不能拖。我的意思是当你按方向键时角色移动逻辑应该直接写在get_pressed()的 if 分支里不要走什么复杂的消息队列也不要加额外的延迟函数。游戏对输入的响应延迟必须控制在 1 帧以内玩家的感觉才会是“敏捷”的这就是为什么很多游戏“手感好”的背后其实只是简单的即时响应。6.3 一点个人经验我在实际做 Pygame 项目的过程中慢慢形成了一个习惯每做一个功能就先跑起来看效果再继续加下一个。不要憋着一口气把所有代码写完才运行那样报错后你根本没有把握定位问题在哪个分区。比如我先让雪球能往下掉跑通再加碰撞跑通再加计分跑通再加重新开始。每个小步骤都是可以验证的这其实也是所有做游戏的人默认的工作方式。还有个经验是关于删除逻辑的。游戏里凡是出现“从列表里删除对象”的地方都要想想遍历会不会出问题。我吃过亏之后就记住了一句话删除和遍历永远不要在同一份列表上直接进行。要么用副本要么先标记后统一删除要么等循环结束后再处理。这个原则救了我很多次希望你也能记住。最后我想说用 Pygame 做你的第一个小游戏它不会让你一夜之间变成游戏开发高手但它会给你一个非常难得的完整闭环从想法到代码从代码到能玩的游戏再到分享给别人玩。这个过程对一个新手来说比任何零散的知识点都更有价值。做完这个躲避雪球之后你可以试着把它改成飞机大战、变成双人比赛、加入武器和道具。你缺的永远不是更好的引擎而是动手把脑中的想法变成可玩作品的那一步。现在是时候打开编辑器把那份完整代码跑起来了。
返回列表