首页 / 资讯中心 / 文章详情

用Pygame开发接水果小游戏:从环境搭建到碰撞检测完整实战

用Pygame开发接水果小游戏:从环境搭建到碰撞检测完整实战 ★ FEATURED ARTICLE
想写一个能真正跑起来、还能拿给朋友玩的小游戏我建议首先考虑Pygame。它不像写网页游戏那样要碰前端那一堆东西也不用绕开“游戏引擎”的复杂概念只要你会一点Python装个库就能把一个窗口弹出来接着画图形、响应用户操作、做碰撞判断最后拼出一个完整的可玩Demo。这篇文章就围绕“用Pygame开发你的第一个小游戏”这个话题展开把从环境搭建、项目拆解到核心代码实现的完整过程梳理一遍适合有Python基础但从来没碰过游戏开发的读者也适合想快速验证一个交互想法的人参考。我会用一场“接水果”的小游戏作为主线来演示。这个玩法足够简单但把游戏循环、键盘事件、碰撞检测、分数系统这些游戏开发的骨架内容全都覆盖到了。看完这篇文章你不仅能自己敲出一个能运行的小游戏还能在同一个框架上继续加关卡、加音效、切场景而不是只会照着教程抄一遍。1. 项目思路与整体设计1.1 为什么第一个游戏用Pygame而不是其他方案市面上能用来写游戏的工具很多比如各种重型商业引擎、网页框架、甚至直接撸底层图形接口。但对刚接触游戏开发的人来说很多方案的学习成本并不在“做游戏”本身而是被引擎的编辑器界面、资源管线、构建流程给拖住了。Pygame最舒服的地方在于它足够“裸”——它只负责给你一个窗口、一个绘图表面、一套事件系统剩下的事情全部由你的代码逻辑来组织。这种极简分工其实很适合学习因为你能清楚看到游戏运行的本质每一帧里发生了什么、什么时候响应按键、什么时候做碰撞计算。游戏引擎的复杂功能反倒会把这种底层逻辑隐藏掉。另外Pygame本身封装的是SDL跨平台支持很好。你在自己电脑上写出来的程序换一台装好Python和Pygame的机器基本上可以原样运行发布成可执行文件也有成熟的打包工具。对于第一个游戏来说这个成本已经低到几乎不需要犹豫了。1.2 游戏循环的底层逻辑游戏程序和我们平时写的脚本有一个关键区别不能从头到尾顺序执行一遍就退出而要一直在“运行中”状态里待着不断重复完成三件事——处理输入、更新游戏状态、重新绘制画面。这个重复过程就叫游戏循环。我习惯用一个比喻来理解它游戏主循环就像一个餐厅服务员。服务员会不断查看每一桌客人是否有什么新需求这相当于“事件处理”如果客人举手了服务员就去下单传菜这相当于“更新状态”传完菜再环顾一下全场有没有新需求这相当于“绘制画面”。这个“查看-响应-刷新”的流程每秒钟要重复几十次也就是所谓的帧率FPS。Pygame里对应的写法非常直白一个while循环加上事件队列、状态更新和画面刷新构成所有游戏的核心骨架。1.3 这次要做成一个什么样的游戏为了让标题里的“第一个小游戏”有可落地的抓手我把项目设计成“接水果”玩法屏幕底部有一个由玩家控制的挡板顶部随机落下水果挡板接到水果得分漏掉水果则损失生命生命用完游戏结束。这个设计的优点有三个。第一个物体数量少不需要复杂的资源管理第二个玩法反馈非常直接——接住立刻加分漏掉立刻扣命用户马上能感受到自己操作的作用第三个它天然包含随机性、难度递增和结果判定不会让人觉得“这一步看不出游戏效果”。整个项目只用到一个图形坐标体系、键盘事件、矩形碰撞检测和文本显示正好是Pygame入门最核心的几块拼图。我倾向于在第一版里不引入复杂的类继承结构直接用一个主循环加一组普通变量来实现。初学阶段最重要的是“跑通”等确认自己理解了循环逻辑之后再把代码重构为精灵类和组反而会清楚很多。2. 环境准备与素材处理2.1 搭建一个适合Pygame的开发环境先说环境。Pygame是用Python写程序最常见的入门游戏库安装方式在Windows、macOS、Linux上都一样直接用pip安装即可pip install pygame装完之后验证一下是否成功python -c import pygame; print(pygame.version.ver)如果能看到版本号说明环境已经就绪。需要注意的一点是Python版本尽量用当前主流的稳定版本避免太老的解释器在安装预编译包时缺文件。Pygame对Python版本比较友好正常不会遇到麻烦但万一安装时碰到依赖错误先升级pip再重试往往就能解决问题。编辑器方面没有强制限制只要能写Python代码的编辑器都可以。我个人不太建议第一个项目就去折腾复杂的IDE因为做游戏时要反复快速修改然后运行轻量编辑器加上终端反而更敏捷。如果你需要调试阶段看变量值顺手在代码里加打印输出往往比断点更快游戏程序循环速度快断点调试有时候会打断思路。2.2 项目目录安排得清爽一些做第一个游戏时很多人会在根目录堆一堆文件以后想加素材就乱了。我建议从一开始就保持一个清晰的目录结构catch_fruit/ ├── main.py ├── assets/ │ ├── images/ │ └── sounds/ └── README.mdmain.py放全部逻辑代码图片和音频素材分别放在images和sounds目录里。这看起来简单但养成这个习惯之后你后续扩展任意项目都会受益。实际上当我把第一版游戏做到中途想换水果图片时如果当时素材乱放可能已经懒得改了目录清晰能直接降低你优化项目的心理阻力。2.3 素材不一定要靠下载先学会用图形绘制做游戏自然绕不开素材。但对第一个小游戏来说我建议先别花太多时间找图片素材。Pygame本身就提供了一组基础绘图函数比如画矩形、圆形、多边形。用它们来代表玩家挡板和下落的水果完全够用而且代码更简洁运行效率也高。如果你就是想用现成图片也没问题。推荐找那种透明背景的PNG图片图片分辨率不要太高一个挡板或者一个水果的尺寸在几十到一百像素左右正好。加载图片时用pygame.image.load方法然后调用convert_alpha把图片格式转换成更适合Pygame逐帧绘制的格式这一步能明显减少渲染时的计算浪费。没有convert_alpha直接加载也能跑但画面频繁刷新时性能会有肉眼可见的差距。音频素材就更简单了游戏音效可以用支持Audio类的短音效文件常见的来源有很多经典小游戏素材库注意版权就行。所有素材文件尽量统一放好代码中读取路径时把路径拼接写对避免在素材目录上犯“路径错了但代码没报错”的低级错误。3. 一步步写出可运行的Pygame游戏3.1 初始化窗口与主循环骨架先完成最基础的窗口初始化。窗口的宽高、标题和帧率是一上来就要确定的配置。以我设计的接水果游戏为例窗口宽度设为600像素、高度800像素标题叫“接水果小游戏”锁定帧率60FPS。代码框架如下import sys import random import pygame # 初始化Pygame pygame.init() # 基本常量 WIDTH 600 HEIGHT 800 FPS 60 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(接水果小游戏) clock pygame.time.Clock() # 主循环入口 running True while running: clock.tick(FPS) for event in pygame.event.get(): if event.type pygame.QUIT: running False # 后续帧逻辑都写在这里 pygame.display.flip() pygame.quit() sys.exit()这段代码就是所有Pygame程序的标准骨架。pygame.init负责初始化所有模块set_mode创建窗口Clock对象控制每秒循环次数。while循环里事件处理之后更新状态最后flip把后台绘制内容真正翻到屏幕上。有个常见误区是忘了flip导致画了东西屏幕却不显示这里一开始就记住所有绘制命令执行完后必须调用flip才会呈现结果。3.2 玩家挡板用键盘操作一个矩形在窗口底部放一个矩形当挡板。挡板需要有初始坐标、宽度和和高度还要能够响应键盘操作。Pygame中矩形用Rect对象表示它存储左上角坐标和宽高通过x和y属性可以随时修改位置。挡板初始位置放在窗口底部中间宽度120像素高度20像素底部距窗口下边缘30像素。响应键盘时我不用按键的“按下瞬间”而用“持续按住”状态。Pygame里有两种常见方式一种在事件循环里捕捉pygame.KEYDOWN按一次只移动一个固定距离另一种是在循环体里读取pygame.key.get_pressed()获取所有按键的持续状态。接水果游戏需要玩家连续控制位置所以用get_pressed更合适。player_width 120 player_height 20 player_speed 8 player_x WIDTH // 2 - player_width // 2 player_y HEIGHT - 50 player_rect pygame.Rect(player_x, player_y, player_width, player_height) keys pygame.key.get_pressed() if keys[pygame.K_LEFT] and player_rect.left 0: player_rect.x - player_speed if keys[pygame.K_RIGHT] and player_rect.right WIDTH: player_rect.x player_speed这里两个边界条件很关键。如果少了左边界判断挡板会直接消失在窗口外如果少了右边界判断挡板会有一半跑到屏幕外。用left 0和right WIDTH这两个判断来保证挡板整体永远不会滑出画面。也许你会问为什么移动速度是8而不是固定像素值因为这里的“速度”实际是“每帧移动的像素数”。在60FPS下每帧移动8像素意味着每秒移动480像素。相对窗口600像素宽来说大约1.25秒可以横穿整个屏幕。这个速度对我这个尺寸的窗口来说手感刚好不会太快难控制也不会太慢让人着急。理解这个关系后你就明白调节速度和调整窗口大小是配套的窗口越大就要相应提高每帧移动像素数否则会觉得挡板像乌龟在爬。3.3 下落水果随机生成、移动与碰撞判定接下来是游戏核心玩法水果从顶部出现并匀速下移。我用一个圆形来代表水果圆形的圆心坐标和半径保持不变只有y坐标每帧增加一个速度值。当一个水果掉出窗口底部时就把它重新放回顶部并随机更新x坐标。更真实一点的做法是在游戏开始时加载一个水果列表同时追踪多个下落物但第一个版本里同时只控制一个水果就能玩起来理解逻辑之后再改成多个。水果的半径固定为20像素生成时圆心x坐标在[20, WIDTH-20]之间随机选择这样它不会从屏幕边缘“挤出去”。下落速度我设计为初始5像素每帧并且它会随着得分增加而加快。核心逻辑fruit_radius 20 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y 0 fruit_speed 5 # 主循环内 fruit_y fruit_speed if fruit_y HEIGHT fruit_radius: fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y 0 fruit_rect pygame.Rect(fruit_x - fruit_radius, fruit_y - fruit_radius, fruit_radius * 2, fruit_radius * 2) if player_rect.colliderect(fruit_rect): score 1 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y 0碰撞检测这里使用了矩形碰撞。虽然水果是圆形但Pygame的Rect碰撞检测是拿两个矩形的边界来判断是否相交。这个判断方式很高效对简单的游戏完全够用。需要注意碰撞检测计算的是矩形而不是圆形这意味着圆形边缘的四个角的区域虽然实际没有水果图像但也参与碰撞判定。对大多数休闲游戏来说这个误差不会有人察觉如果你想更精确后续可以自己计算两个圆心之间的距离与半径之和的关系不过对第一版来说没必要。还有一个细节水果超出屏幕底部后除了重新设置位置最好把x坐标重新随机一次。否则你会看到水果消失后沿着同一个x位置再掉下来玩法就会变得单调。在原有基础上再增加一点难度曲线让fruit_speed随得分递增fruit_speed 5 score // 5score每增加5分下落速度提升1像素每帧。这个递增节奏对新手来说不会太快失控又让人明显感觉到压力增加。3.4 得分、生命与游戏结束状态得分和生命是游戏反馈的核心。需要定义score为0life为3。每次碰撞成功score加1每次掉出屏幕life减1。当life减少到0时游戏不能继续应进入一个“游戏结束”状态。对于状态管理第一版用布尔变量game_over控制即可。游戏结束时不更新物体位置只显示一段文本同时给玩家提供“重启”入口。重启最简单的实现是当game_over为True时检测键盘按下的R键重置所有数据。if game_over: # 显示结束文字 # 按R重置 keys pygame.key.get_pressed() if keys[pygame.K_r]: score 0 life 3 fruit_speed 5 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y -fruit_radius player_rect.x WIDTH // 2 - player_width // 2 game_over False这种单变量状态管理很清楚但它有一个缺点当游戏玩法变复杂之后散落的变量会让重置逻辑越来越难维护。到了后面想加多个关卡、暂停、弹出菜单时就需要用一个状态字典或者类来收拾这些状态。第一版先不引入这些复杂度还是先把核心流程打通重要。3.5 画面绘制与帧率控制绘制顺序在Pygame里也很重要。每一帧要先把整个屏幕填充为背景色然后才画其他元素。否则上一帧的画面会残留在窗口上形成拖影或者重影效果。绘制时先填充背景再画水果、挡板、分数和生命最后刷新。文字绘制有固定套路先创建字体对象然后用.render方法生成一个包含文字的Surface再把这个Surface放到屏幕坐标上。Pygame自带的SysFont字体可以避免加载外部字体文件的麻烦score_text font.render(f分数: {score}, True, (255, 255, 255)) screen.blit(score_text, (10, 10))blit这个词直译是“拷贝块”可以把一个Surface的内容绘制到另一个Surface上。把文字Surface放到屏幕的某个坐标就完成了文字显示。每次调用fill和blit后最后的pygame.display.flip()才把这些内容统一呈现。帧率控制通过clock.tick(FPS)完成。它的作用是限制主循环每秒最多执行多少帧。这个控制不只是为了让画面流畅还直接关系到速度数值的一致性。如果不限制帧率在120Hz高刷新率显示器上游戏速度会比60Hz上快一倍物体的移动会肉眼可见地加速。所以写游戏一定要给游戏循环套上Clock节流。3.6 完整代码一个能玩的小游戏把以上零散片段合并成一个完整可运行的main.py。完整代码如下import sys import random import pygame pygame.init() WIDTH 600 HEIGHT 800 FPS 60 screen pygame.display.set_mode((WIDTH, HEIGHT)) pygame.display.set_caption(接水果小游戏) clock pygame.time.Clock() player_width 120 player_height 20 player_speed 8 player_rect pygame.Rect(WIDTH // 2 - player_width // 2, HEIGHT - 50, player_width, player_height) fruit_radius 20 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y 0 fruit_speed 5 score 0 life 3 game_over False font pygame.font.SysFont(microsoftyahei, 32, boldTrue) small_font pygame.font.SysFont(microsoftyahei, 36, boldTrue) running True while running: clock.tick(FPS) for event in pygame.event.get(): if event.type pygame.QUIT: running False keys pygame.key.get_pressed() if not game_over: if keys[pygame.K_LEFT] and player_rect.left 0: player_rect.x - player_speed if keys[pygame.K_RIGHT] and player_rect.right WIDTH: player_rect.x player_speed fruit_y fruit_speed if fruit_y HEIGHT fruit_radius: life - 1 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y -fruit_radius if life 0: game_over True fruit_rect pygame.Rect(fruit_x - fruit_radius, fruit_y - fruit_radius, fruit_radius * 2, fruit_radius * 2) if player_rect.colliderect(fruit_rect): score 1 fruit_speed 5 score // 5 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y -fruit_radius else: if keys[pygame.K_r]: score 0 life 3 fruit_speed 5 fruit_x random.randint(fruit_radius, WIDTH - fruit_radius) fruit_y -fruit_radius player_rect.x WIDTH // 2 - player_width // 2 game_over False screen.fill((30, 40, 60)) pygame.draw.circle(screen, (220, 80, 60), (fruit_x, fruit_y), fruit_radius) pygame.draw.rect(screen, (240, 200, 80), player_rect) if not game_over: score_img font.render(f分数: {score}, True, (255, 255, 255)) life_img font.render(f生命: {life}, True, (255, 255, 255)) screen.blit(score_img, (10, 10)) screen.blit(life_img, (10, 50)) else: over_img small_font.render(游戏结束, True, (255, 80, 80)) over_rect over_img.get_rect(center(WIDTH // 2, HEIGHT // 2 - 40)) screen.blit(over_img, over_rect) restart_img small_font.render(按 R 重新开始, True, (200, 200, 200)) restart_rect restart_img.get_rect(center(WIDTH // 2, HEIGHT // 2 20)) screen.blit(restart_img, restart_rect) pygame.display.flip() pygame.quit() sys.exit()这段代码保存后直接运行就能看到一个可以玩的接水果游戏了。Windows系统上字体名可以写成microsoftyahei如果本机没有这个字体可以把SysFont的字体名改成None让它选择默认字体。这个版本里我把字体选择直接写成了中文字体所以要确认系统里是否存在对应字体。在macOS或Linux上若没有可以改用通用字体名如simhei或wqysong或者直接不传字体名让Pygame自己挑。字体这块是第一个小游戏最容易在跨平台时出现“中文变方块”的地方。# 更稳妥的做法 font pygame.font.Font(None, 36)Font(None, size)会加载默认字体虽然中文可能显示为方块但至少不会因为字体名不存在而崩溃。如果你一定要显示中文并保持美观在项目目录里放一个开源中文字体文件用pygame.font.Font(path/to/font.ttf, size)加载跨平台就彻底解决了。3.7 为什么碰撞检测里Rect这么方便用Rect对象做碰撞是Pygame里最常用的技巧之一。每个Sprite或者游戏物体都可以用Rect来表示位置和佔据空间。它会自动提供left、right、top、bottom这些属性而且支持colliderect直接判断两个矩形是否相交。使用Rect时有一个很容易忽略的点Rect的x和y表示的是矩形的左上角坐标而不是中心点坐标。绘制圆形时我们习惯用圆心坐标绘制矩形时习惯用左上角坐标所以在代码里我把水果的圆心坐标换算成矩形左上角坐标来构建水果Rect再拿它和挡板Rect做碰撞。如果你直接把圆心坐标当成矩形坐标做碰撞会发现水果还没碰到挡板边缘就被判定接住了或者右侧一大块没有水果图像的区域也能得分。这个问题我在早期项目里犯过好多次排查时就看Rect的尺寸和位置对齐方式是否和实际绘制内容一致。4. 开发过程中容易踩的坑4.1 窗口能打开但画面黑屏或没反应最开始写完代码运行很常见的情况是窗口能打开但屏幕上什么都没有按键也没反应。这种问题通常有个规律主循环没有在跑或者画面没有刷新出来。如果你把print(tick)加在while循环里发现控制台没有任何输出那大概率是循环根本没有进入或者程序在进入循环前已经阻塞了。另一个低频但隐蔽的原因是pygame.display.set_mode之后没有保存返回值。Pygame允许代码不保存screen对象照常运行但你在调用pygame.draw时会画到默认的屏幕上之后你怎么对齐都奇怪。严格来说这是写法不规范保存返回值并在所有绘制代码中使用同一个Surface是最可靠的习惯。4.2 按下一次按键到底该移动多远用户按住方向键时挡板应该连续移动而不是只动一下。我发现很多新手第一版代码是这样的for event in pygame.event.get(): if event.type pygame.KEYDOWN and event.key pygame.K_LEFT: player_rect.x - player_speed这样写的问题是KEYDOWN事件只在按键被“按下去”的那一帧触发一次。如果在60FPS下按住左键实际上挡板会在按下瞬间移动一帧然后停住不再动直到松开重新按才再动一次。没有经验的人会觉得挡板“卡顿”“不听使唤”。解决方案就是用pygame.key.get_pressed()去读取当前的按键状态而不是监听KEYDOWN事件。这个区别是Pygame里感知“持续按住”和“瞬间按下”的分界线。接水果这种需要连续控制的场景用get_pressed菜单选择之类的交互场景用KEYDOWN更合适。4.3 图片加载后出现奇怪的黑色方块在加载非PNG图片或者透明PNG时出现黑色背景是很经典的问题。常见原因是没有正确处理图片的alpha通道。用pygame.image.load加载图片后返回的Surface如果图片本身带透明通道最好调用convert_alpha转换格式否则Pygame在逐像素绘制时会用不正确的通道信息透明区域就变成了不透明的黑块。另外还要注意文件格式。JPG格式本身不支持透明通道如果拿一张JPG图片来做透明素材无论如何也抠不出透明背景。做游戏素材尽量统一用PNG并且确认图层里确实有透明区域而不是白色背景冒充透明。图片尺寸过大也会影响性能一个1024x1024的水果图片在60帧循环里反复blit缩放会消耗不少计算。不要直接在加载后塞一个巨大图片必要的时候用pygame.transform.scale缩到目标尺寸。4.4 音效播放时出现噪音或速度异常Pygame在初始化混音器前就播放音频会报错或者没有声音。标准做法是在pygame.init之后调用pygame.mixer.pre_init设置采样率和声道数再重新初始化。如果你的音频文件采样率和Pygame默认设置不一致会出现播放速度异常的情况听起来要么音调太高要么像慢动作回放。奶瓶里播放音效用的预初始化写法pygame.mixer.pre_init(44100, -16, 2, 512) pygame.init()这样设置会让混音器按照44.1kHz采样率初始化和大部分音效素材的原始参数保持一致。播放时用pygame.mixer.Sound加载WAV文件重复播放调用它的play方法不会对主循环造成阻塞。不建议直接用pygame.mixer.music来播放短音效music模块更适合长时间背景音乐短音效用Sound更合理。4.5 高刷新率显示器上游戏速度翻倍前面已经提到必须用clock.tick(FPS)锁帧。不锁帧时Pygame会以电脑的最大能力去刷新循环刷新率越高update逻辑执行次数越多物体移动越快。这会让同一个游戏在不同设备上体验完全不同。锁帧之后Game循环的迭代速率被限制在每秒60次物体的速度就有了统一的参照基准。但要理解一点锁帧锁的是每秒“循环执行次数”不是你电脑的硬件刷新率。如果显示器是144Hz剩下的刷新周期会被空转或者产生撕裂驾驶上没影响。对新手阶段来说完全不用纠结刷新率同步的问题锁帧是最容易且最稳定的方案。4.6 常见问题排查速查表问题现象可能原因排查和解决思路窗口黑屏主循环未进入或未调用flip在循环里加print确认运行检查pygame.display.flip是否执行按键移动不连续使用KEYDOWN处理持续移动改用pygame.key.get_pressed()读取状态图片带黑底透明通道没处理好改成PNG透明素材加载后用convert_alpha游戏速度随设备变化没有锁帧或Fast规则不对加上clock.tick(FPS)统一速度定义水果穿过挡板Rect位置对齐错误或每帧移动过快绘制和Rect用同一坐标系下调速度或缩小位移帧点击关闭按钮没反应没有处理QUIT事件在事件循环中捕获pygame.QUIT设置runningFalse中文显示为方块系统没有指定字体在目录放开源TTF字体用Font绝对路径加载运行报错提示未初始化没有执行pygame.init()在顶部先初始化再创建窗口和音频这张表是我自己做项目时最常用到的排查索引。问题定位的时候先看是渲染、事件还是循环逻辑不要在单一环节死磕。5. 从“能玩”到“好玩”的扩展路径5.1 使用精灵类重构项目当基础版本跑通后如果你还想继续加功能第一件事建议是把代码重构为使用pygame.sprite.Sprite。把挡板和水果分别写成子类每个类里定义update方法然后全部放进Group统一管理和绘制。这样做的收益并不是让游戏更好玩而是让你的代码更方便维护。我之前做过一个类似的教训刚开始所有变量都写在主循环里加了三个不同下落物之后循环里的重复代码变成一大坨加新功能得小心翼翼怕弄错变量。后来下决心改成精灵和组虽然前期多花了一点时间但后面加特效、加敌人、加道具都变得非常顺手。用类和Group管理多个物体之后“接触到了”碰撞检测部分可以统一处理不再一个个if去判断。5.2 加入音效、难度曲线和道具从好玩程度上看目前版本只做了最基础的玩法框架。比较值得优先加的功能有三个。第一个是音效接住水果时播放一个清脆的短音漏接时播放一个低沉的提示音即使只有两个音效游戏体验也会立刻上一个档次。第二个是更平滑的难度曲线现在每5分提升一次速度更合理的做法是让速度按时间连续递增而不是突变。第三个是道具系统比如金色水果得分翻倍灰色水果接住扣命这会带来策略选择让玩家愿意反复挑战。道具系统本质还是复用碰撞检测和随机生成逻辑。给水果加一个type字段区分不同种类绘制时按类型选颜色计分时读type做不同操作。这个思路简单但扩展性很强。如果想再进一步还可以加道具特殊效果比如挡板变宽、减速不过那就需要引入计时器和临时状态了。5.3 打包发布让朋友也能玩游戏在你自己电脑上能跑还不够如果想发给朋友玩需要打包成独立可执行文件。Python打包游戏的常用工具是PyInstaller。以当前项目为例在main.py所在目录执行pip install pyinstaller pyinstaller -F -w -n catch_fruit main.py-F表示打包成单个文件-w表示运行时不弹出黑色控制台窗口。打包完成后dist目录下会生成catch_fruit.exe拷贝给别人的时候注意把assets目录一起带上因为代码里以相对路径读取素材如果缺了目录加载图片时程序就会崩溃。这里还有一个小坑打包时如果素材路径用的是绝对路径程序在你电脑上没问题但发给别人时会导致读取失败。从一开始就在项目里用相对路径并保证main.py的所在目录和assets目录的相对位置固定能省去大量打包后的故障排查时间。第一次打包遇到杀毒软件误报是常态不用太担心。给同学的版本可以换个更整洁的图标和游戏名字让作品看起来更完整。做到这一步你已经不是在“练习写代码”而是真正做完了自己的第一个可发布小游戏。6. 我做这种小项目时攒下的几个心得第一次做出能玩的小游戏后还有几个做事顺序上的建议给后来者。我现在做类似项目时会先花十分钟把玩法写在一张纸上包括分数规则、失败条件、用户能做什么操作。不要觉得这是多余的哪怕写三行那个“目标感”也会在写代码时帮你避掉很多无效改动。真正动手时先把主循环架子搭好确认空窗口能正常打开关闭再往里加第一个功能接着马上运行看效果。这样每一次改动都看得到变化而不是憋一个巨大的完整代码后到处报错。另外调试小游戏的时候别急着把代码拆得特别花哨。第一版能跑最重要格子写清楚变量名不要省字数等确认功能正常了再重构。很多卡了好久的Bug原因就藏在“当时为了省事而简写的那一行”里。还有一点是多利用Pygame本身提供的能力能用绘图函数表达的就不要一定加载图片。第一版做出来之前要保存的只是“能动、能玩、逻辑完整”而不是“美术漂亮、声音齐全”。如果这篇文章能帮你把第一个Pygame小游戏跑起来那接下来最好的下一件事就是动手改代码。把速度调快、把水果换成方块、再加一个接住炸弹就扣分的规则都是在同一个循环体系里加水加盐。游戏开发入门就这样——先跑通一个闭环然后把每一个令你好奇的想法都变成看得见的改变。
阅读完成 · 觉得有帮助?
咨询建站