ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Python pygame 开发超级马里奥:从零到打包发布完整教程

2026/9/2 19:19:15 拓冰建站 浏览量
Python pygame 开发超级马里奥:从零到打包发布完整教程 简介使用Python与Pygame实现的超级马里奥第一关源码项目定位为有一定Python语法基础、想通过真实游戏案例进阶编程能力的学习者也可作为毕业设计或趣味编程练手素材。项目将Player、Enemy、Platform等角色抽象为类完整覆盖事件监听、碰撞检测、跳跃物理、得分统计、多关卡地图切换、背景音乐与音效播放等游戏开发核心机制。压缩包共91个文件含29个Python脚本、24个pyc编译文件、18个ogg音效、9张png图像、5个wav音频及字体、说明文档等整体约9.52MB目录结构清晰。目前已有3855人学习下载是Python游戏入门的高人气案例。通过阅读和修改源码可以掌握Pygame精灵绘制、游戏状态管理、资源加载与关卡扩展的具体写法理解面向对象设计在游戏项目中的落地方式为独立开发小型游戏积累可复用经验。 最近有朋友问我能不能用 Python 写一个超级马里奥说实话这个问题我在刚开始学 Python 的时候就想过。用 pygame 做一个横版跳跃闯关游戏几乎是每个 Python 游戏开发爱好者绕不开的练手项目但网上大部分教程要么只贴一段残缺代码要么就是照着素材硬抄真正把思路讲透的很少。这篇就结合我实际做过的版本从整体设计到核心代码再到打包发布把整条链路完整拆一遍。这个项目适合谁如果你已经会 Python 基础语法、想进阶实战或者想彻底搞懂游戏循环、碰撞检测、状态机这些概念在代码里到底怎么落地那照着这篇自己敲一遍收获会非常扎实。就算你刚入门只要耐心把环境配好跟着流程走也能跑起来重点是理解背后的逻辑而不是背代码。1. 项目整体设计与思路拆解1.1 为什么选择 pygame 而不是其他方案做马里奥这种横版动作游戏Python 生态里最成熟的方案就是 pygame。也有人会用 arcade、cocos2d 或者 tcod但 arcade 在国内教程少cocos2d 偏商业tcod 适合做 Roguelike 回合制游戏。pygame 的好处是封装了窗口创建、图像加载、音频播放、事件处理这些底层逻辑同时又保留了逐帧绘制和手动更新游戏状态的能力自由度非常高刚好能覆盖马里奥这种对精确控制有要求的游戏类型。还有一个容易被忽略的点pygame 的资料密度极高就算你遇到冷门 bug搜索引擎也基本能搜到答案。对于学习型项目可查性比性能更重要。我的建议是如果你只是想做一个小游戏练手pygame 是首选如果你要做高精度的 60 帧动作游戏再考虑 Godot 或者用 Python 写核心逻辑但把渲染交给别的引擎。1.2 马里奥游戏的核心需求拆解一个能被称为“超级马里奥”的游戏至少需要以下能力角色控制左右移动、跳跃、下落并且跳跃要有“上升变速 下降加速”的物理手感。碰撞检测角色能站在平台上能被墙壁挡住能顶砖块能踩敌怪。敌人逻辑蘑菇怪Goomba巡逻、被踩死、碰到角色则角色受伤或死亡。摄像机跟随地图比屏幕大摄像机要平滑跟随角色。关卡设计多个平台、砖块、水管、终点旗杆最好还有背景重复滚动。状态管理待机、跑动、跳跃、死亡、过关等状态切换。这些需求组合起来核心难点其实是两块物理系统、碰撞处理。很多新手写出的马里奥“手感不对”就是因为没有把这两块处理好。碰撞和物理是游戏开发的“心电图”心跳对了游戏才活着。2. 环境准备与工具链配置2.1 Python 安装与虚拟环境创建如果你电脑里还没装 Python先从官网下载安装包一定要勾选“Add Python to PATH”我见过太多人的问题就出在这。建议装 Python 3.10 或 3.11 这种稳定版本不要追最新的 3.13有些第三方库的预编译包可能还没跟上。装完之后建议给这个项目建一个虚拟环境免得把全局环境搞乱。我用的是 Python 内置的 venvpython -m venv mario_env激活虚拟环境Windows 下执行mario_env\Scripts\activatemacOS/Linux 下执行source mario_env/bin/activate激活后命令行前缀会变成 (mario_env)然后安装依赖pip install pygame如果网络慢可以加国内镜像源pip install pygame -i https://pypi.tuna.tsinghua.edu.cn/simple装 pygame 这个库是最核心的它让 Python 有了做游戏的能力相当于给了你一个可以画图、播放声音、读键盘、管理游戏循环的工具箱。没有它你要自己面对窗口系统和图形 API那学习成本就太高了。2.2 IDE 选择与运行调试配置编辑器方面我日常用的是 VS Code搭配 Python 插件。项目目录打开后需要把 Python 解释器指到刚才创建的虚拟环境上按 CtrlShiftP输入 “Python: Select Interpreter”选择 mario_env 里的 python.exe。如果是新手PyCharm 社区版也很合适它新建项目时直接支持配置虚拟环境省事。不推荐用记事本或者系统自带编辑器写这种项目因为你需要单步调试、看变量值好的编辑器能把调试体验拉满。调试时有个小技巧在游戏代码中设置断点时不要把断点打在游戏循环内部每一帧都执行的地方否则每帧都要停下来几乎无法调试。正确做法是在条件成立的地方打断点比如“检测到碰撞时”或者“生命值为0时”用条件断点效率高得多。3. 核心代码结构与实现细节3.1 游戏框架主循环与事件处理pygame 游戏的骨架是一个“永不停止的 while 循环”加“事件驱动”。理解这个循环是理解 pygame 的钥匙。先看一段最精简的框架代码import pygame pygame.init() screen pygame.display.set_mode((800, 600)) clock pygame.time.Clock() running True while running: # 1. 处理事件 for event in pygame.event.get(): if event.type pygame.QUIT: running False # 2. 更新游戏状态 # 3. 绘制画面 pygame.display.flip() clock.tick(60) pygame.quit()这个循环里藏着三个关键步骤。第一步处理事件比如“点关闭”“按键盘”第二步更新状态比如马里奥的位置、速度、动画帧号第三步绘制画面把新的状态画到屏幕上。clock.tick(60) 的意思是限制循环每秒最多跑 60 次保证游戏在不同电脑上速度一致这就回答了“为什么同样的代码在我的电脑上飞快”的问题——不加帧率限制游戏运行速度就会跟 CPU 性能绑定。3.2 精灵类:马里奥、敌人、砖块的共同父类pygame.sprite.Sprite 是 pygame 提供的精灵基类它封装了图像、矩形位置、更新方法。把马里奥和敌人都做成 Sprite 的子类之后可以用 sprite.Group 来统一管理和渲染还能直接调用 pygame.sprite.groupcollide 这类碰撞检测函数。定义马里奥类时我用一个字典来管理不同动作对应的动画帧这样扩展性比堆 if-else 好得多class Mario(pygame.sprite.Sprite): def __init__(self, x, y): super().__init__() self.frames { idle: [...], # 待机动画帧列表 run: [...], # 跑步动画帧列表 jump: [...], # 跳跃动作帧 } self.state idle self.frame_index 0 self.image self.frames[self.state][0] self.rect self.image.get_rect(topleft(x, y)) self.vx 0 self.vy 0 self.on_ground False这个方法的核心思路是“状态驱动动画”马里奥当前处于什么状态就播放什么动画。加新动作时只需要新增状态和对应帧不需要改动主循环的判断逻辑。状态机思想在游戏开发里无处不在不只是马里奥任何角色动作、AI 状态都能套用。3.3 物理与手感跳跃的重力机制马里奥手感好不好全靠跳跃物理写得好不好。我的实现经验是跳跃不能简单设一个向上速度就完事要区分“上升段”和“下落段”并且重力在上、下行时不同。上升时重力小一点下落时重力大一点这样跳起来有“飘”的感觉落下来干脆利落手感会好很多。GRAVITY_UP 0.5 GRAVITY_DOWN 1.2 JUMP_SPEED -15 # 在 update 里处理跳跃 if self.vy 0: self.vy GRAVITY_UP else: self.vy GRAVITY_DOWN self.rect.y self.vy这样处理后跳跃曲线不是对称抛物线而是“缓起快落”这是很多经典横版游戏手感的核心之一。数值具体调多少需要反复试我的建议是先把 gravity 设为 0.8、JUMP_SPEED 设为 -14 作为初始值跑起来后按个人感受调没有标准答案。这就是游戏开发里说的“调参”早期不必追求完美先跑通再调手感。3.4 摄像机跟随与背景滚动有了地图就涉及镜头。摄像机本质上是一个偏移量绘制所有物体时都减去这个偏移量。最简单的跟随是直接“锁定角色”但这样镜头会非常生硬。平滑一点的方案是用 lerp线性插值让镜头缓慢追向目标camera_x (mario.rect.centerx - WIDTH // 2 - camera_x) * 0.1这个公式要注意camera_x是平滑跟随后的偏移量mario.rect.centerx - WIDTH // 2是理想偏移量* 0.1是每帧移动 10% 的距离值越小越“肉”。实际使用中如果 0.1 太拖沓可以改成 0.2 或者 0.25。镜头还需要加边界限制不能让它移出地图范围camera_x max(0, min(camera_x, MAP_WIDTH - WIDTH))背景滚动可以做“视差滚动”就是远景色块移动速度比近景慢增强立体感。比如天空云朵的偏移量是 camera_x * 0.3近处树木是 camera_x * 0.7地面是 camera_x * 1.0。这个效果实现不难但做成后整个画面的质感立刻不同。3.5 碰撞检测的两种策略碰撞检测是马里奥项目里最折磨人也最容易出错的部分。我实际用过两种方案各有利弊第一种是使用 pygame.sprite.groupcollide / spritecollide 函数自动检测适合“角色 vs 敌人”“子弹 vs 砖块”这种只要判断是否相遇的场景。优点是代码简洁缺点是它只能告诉你“碰没碰”不能告诉你“从哪个方向碰的”这在做“顶砖块”和“踩死敌人”时不够用。第二种是手动逐轴检测把运动拆成 x 轴和 y 轴分别处理先移动 x再检测颜色碰撞并修正然后移动 y再检测并修正。这种方法能精确知道碰撞方向是横板游戏的标准做法。我这里给出一个简化版的逐轴碰撞核心逻辑# 先水平移动 self.rect.x self.vx for block in blocks: if self.rect.colliderect(block.rect): if self.vx 0: self.rect.right block.rect.left elif self.vx 0: self.rect.left block.rect.right self.vx 0 # 再垂直移动 self.rect.y self.vy for block in blocks: if self.rect.colliderect(block.rect): if self.vy 0: self.rect.bottom block.rect.top self.on_ground True elif self.vy 0: self.rect.top block.rect.bottom self.vy 0顺序上一定要先水平后垂直不然会发生斜向卡墙的问题。这个代码还有一个隐藏细节落地时设置self.on_ground True这样角色才能判断“能不能起跳”。如果没做这个标志位角色在空中也能无限连跳那玩起来的感受就完全不是马里奥了。3.6 敌人与状态管理敌人我写了一个 Goomba 类基本逻辑是在平台上巡逻碰到墙壁转向碰到边界掉头。被马里奥从上方踩到时播放“被踩扁”动画然后移除。从侧面撞到马里奥时马里奥进入受伤状态。如果马里奥处于无敌帧期间敌人直接穿过进入“弹开”逻辑。为了保证踩敌判定准确不能简单用矩形重叠判断要判断“马里奥的 y 速度大于 0”且“马里奥矩形底部在敌人矩形顶部偏上一点的位置”。我实际用的是if mario.rect.bottom - enemy.rect.top 20 and mario.vy 0: enemy.kill() mario.vy -8 # 踩敌后弹起这个20像素是经验值给判定留了容错空间玩家感受上更宽容。如果严格按mario.rect.top enemy.rect.top来判断很容易出现“明明踩上去却被敌人伤到”的挫败感。状态管理方面我用的是一个简单的有限状态机马里奥有idle、running、jumping、dead四种状态死亡后禁用玩家输入播放死亡动画两秒后重开本关。这个状态机不需要写复杂的类用字典映射 一个 state 变量就足够了。4. 素材处理、打包发布与常见问题4.1 素材来源与像素风处理做马里奥项目最大的拦路虎之一是没有素材。我实际用的方案有几种在 itch.io 上搜索 “free pixel art platformer”很多素材包允许免费使用注意看一下授权条款。用 OpenGameArt 找经典像素素材相对规范。如果只想先跑通逻辑可以先用色块代替用 pygame.draw.rect 画矩形把马里奥画成一个红色矩形敌人画成棕色矩形砖块画成橙色矩形。等逻辑全部调通再替换素材。这里分享一个我踩过的坑有些像素素材尺寸很大放进去后角色比例失衡感觉不像马里奥而是巨人族。解决办法是统一缩放用pygame.transform.scale把角色控制在 40x40 或 48x48 像素左右砖块用 32x32 或 40x40 比较协调。配上一套简单的背景色天空蓝和像素云朵画面感觉一下子就对了。4.2 用 PyInstaller 打包成 exe想把自己写的游戏发给朋友玩不能让人家装 Python 环境所以打包是必须的。我用的是 PyInstaller装依赖pip install pyinstaller然后打包命令pyinstaller -F -w -n mario main.py --add-data assets;assets参数说明-F是生成单文件 exe方便分发但启动会稍微慢一点。-w是去掉控制台窗口这样运行游戏时不会弹出黑框。--add-data是把素材文件夹打包进去Windows 下分号和冒号的区别要注意Windows 用分号macOS/Linux 用冒号。打包成功后exe 在dist目录下。打包出来的文件体积通常会超过 30MB其中大部分是 pygame 和相关运行库这个正常不用担心。如果你发现打包后缺素材就是用--add-data的时候写错路径或者代码里的路径写的是相对路径问题建议在代码中这样获取路径import sys import os def resource_path(relative_path): base_path getattr(sys, _MEIPASS, os.path.dirname(os.path.abspath(__file__))) return os.path.join(base_path, relative_path)然后用resource_path(assets/mario.png)来加载素材。这样打包前后都不会出现找不到文件的问题。4.3 常见报错与调试技巧我在开发这个项目的过程中整理的几个高频 bug 和排查思路现象可能原因解决方案窗口打开后闪退素材路径错误用 resource_path 或检查文件是否在正确目录角色卡在墙里碰撞检测顺序不对确认先水平再垂直且修正后立刻停止速度跳跃手感很怪、掉帧未限制帧率或者重力数值不合适确保调用了 clock.tick(60)然后调重力参数敌人踩不死踩踏判定条件过严用mario.rect.bottom - enemy.rect.top 20放宽判定pygame 安装失败Python 版本太新或者缺少编译环境换 3.10/3.11 稳定版或用预编译 wheel还有一个容易被忽略的问题pygame 的display.flip()和display.update()的区别。前者是全屏刷新后者可以只刷新指定区域。游戏画面简单时用哪个都一样但如果画面复杂开始掉帧可以试试只在角色变化区域调用 update能省不少性能。不过坦白讲这是优化阶段可以考虑的事第一版不用太较真。4.4 性能优化与代码可维护性游戏开发到了一定阶段就要考虑代码组织了。我自己的做法是用数据配置关卡关卡地图不要硬编码在代码里而是用二维数组或 JSON 来表示。比如T表示砖块、P表示水管、G表示敌人、M表示出生点加载时循环遍历把对应对象实例化出来。这样调关卡布局根本不用改代码改数据就行。把常量和配置放到一个config.py里比如屏幕宽高、重力、跳跃力度、颜色值、音效开关。以后调参只碰这一个文件不会在代码里翻来翻去。把马里奥、敌人、砖块分成不同的模块文件而不是全部堆在一个 main.py。模块化最大的好处是“每改一处都知道自己在哪”这个习惯到了更大的项目里就是救命稻草。5. 扩展方向把它变成自己的游戏如果你已经能让马里奥跑起来、跳起来、踩死敌人那恭喜你核心的战斗已经打赢了。接下来可以按自己的兴趣继续扩展给马里奥加金币收集系统做一个计分 UI加入水管传送功能让角色进入水管后出现在另一个坐标设计一个简单的关卡切换流程过关后进入下一关把敌人 AI 升级一下让飞行乌龟可以有规律地飞行、跳跃巡逻。我自己的经验是加功能之前先想清楚一个问题这个功能会影响哪些系统比如加“金币”就要改 HUD 渲染、碰撞检测、音效触发还要考虑死亡后金币是否会重置。想清楚这个才不会把代码越改越乱。这就是“架构”意识不是只属于大项目小游戏一样需要。我个人在写这个项目的过程中体会最深的一点是游戏开发的核心不是代码本身而是“手感”和“细节”。每项物理参数都可能榨干你的耐心但当你调出一个跳上去刚刚好的平台、踩中敌人时刚好能弹起来的节奏那种成就感是纯业务代码给不了的。建议你先跑通主流程再慢慢调细节。每个参数多试几次你会逐渐建立起“游戏手感”的直觉。如果卡住了就停下来看看是你哪个决策点做的不够清晰而不是死磕代码。你写完这版马里奥再回头看自己第一天的代码会发现进步比想象中大得多。本文还有配套的精品资源点击获取