资讯动态

C++ Qt复刻超级玛丽:源码解析与二次开发实战

发布时间:2026/10/7 1:27:49 来源:尧图企业网站定制
简介使用C编写的超级玛丽小游戏源码是一套适合C初学者与游戏开发入门者的完整工程基于Qt框架实现涵盖角色控制、碰撞检测、道具与关卡切换等核心机制。压缩包内共81个文件大小约8.42MB以43张PNG图片素材为主包含马里奥、敌人、砖块、管道、城堡等游戏元素14个CPP源文件与13个头文件构成逻辑主体另有.pro工程文件、.ui界面文件、.qrc资源文件、README与LICENSE素材与代码分离工程结构清晰便于按模块研读。目前已有7171人浏览学习。通过这份源码读者可获得一套可直接运行、便于二次开发的游戏原型既能直观理解Qt/C项目组织方式也可在此基础上调整参数、替换素材或扩展关卡对提升C面向对象编程及简单游戏开发能力很有帮助。1. 用 C 和 Qt 复刻超级玛丽这份 C 源码包拿回来能不能直接跑如果你下载过 C 小游戏源码大概率遇到过这样的情况压缩包解压出来一堆 .cpp、.h 和资源文件打开 README 也没写清楚怎么编译最后只能放弃。这份超级玛丽源码不太一样它用的是 C 搭配 Qt 框架工程文件、资源文件、界面代码都齐整理论上拿到手配置好 Qt 环境就能直接跑。源码里包含了完整的游戏场景类 gamescene、玛丽角色类 mary、砖块、蘑菇、火焰、管道、城堡等对象主菜单、帮助界面、暂停功能也都做全了。适合有 C 基础、想用 Qt 做课程设计或者单纯想研究经典平台跳跃游戏是怎么实现的人。2. 拆工程骨架先看懂文件组织再动手改代码2.1 从 .pro 和 .qrc 入手快速定位核心代码拿到一个 Qt 工程第一步不是打开 .cpp 挨个读而是先看 .pro 工程文件。super_mary.pro 里定义了项目包含哪些源文件、依赖哪些 Qt 模块这直接决定了程序能编译成什么样子。常见做法是把所有源文件列进 SOURCES 和 HEADERS资源文件挂在 RESOURCES 里整个工程的构成一眼就能看全。# super_mary.pro 的常见结构关键项示意 QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET super_mary TEMPLATE app CONFIG c11 SOURCES main.cpp \ mainwindow.cpp \ gamescene.cpp \ mary.cpp \ brick.cpp \ unknown.cpp \ mushroom.cpp \ fire.cpp \ pipe.cpp \ castle.cpp \ master.cpp HEADERS mainwindow.h \ gamescene.h \ mary.h \ brick.h \ unknown.h \ mushroom.h RESOURCES res.qrc这段配置说明几件事。QT core gui是 Qt 的基础模块从 Qt 5 开始界面控件被独立到 widgets 模块所以有greaterThan那一行做版本判断。TARGET指定生成的可执行文件名编译产物就叫 super_mary。RESOURCES指向 res.qrc工程里所有背景图、角色贴图、按钮图标都是通过这个文件注册进程序的这个文件直接决定了运行界面长什么样。再看 res.qrc。这个文件是 Qt 的资源索引本质上是一份 XML 映射表把素材文件路径和代码里使用的虚拟路径绑定。源码包里那些 background1.jpg、start.png、walk_right1.png、mushroom1.png 全部要在 qrc 里注册代码里才能用:/image/start.png这样的字符串访问。如果图片没有注册或者路径大小写不一致编译不会报错但运行后界面上就是一片空白。2.2 核心类分工谁在管界面谁在管游戏逻辑从文件列表能看出这个项目按对象拆类这是 Qt 游戏最常见的组织方式。mainwindow.cpp 是主菜单窗口负责开始游戏、进入帮助、退出gamescene.cpp 是游戏主场景玛丽、砖块、道具、敌人全部由它创建和管理mary.cpp 封装角色处理键盘输入、移动、跳跃和碰撞后的状态变化。这种拆法对阅读源码特别友好你想找哪个功能的实现直接去对应的类里翻就行。用一张表把主要文件说清楚文件职责说明main.cpp程序入口创建 QApplication显示主窗口mainwindow.cpp/h主菜单界面放开始、帮助、设置按钮gamescene.cpp/h游戏场景创建游戏对象处理对象间交互mary.cpp/h角色控制移动、跳跃、碰撞、死亡状态brick.cpp/h普通砖块可被撞击部分可顶碎unknown.cpp/h问号砖撞击后生成金币或蘑菇mushroom.cpp/h蘑菇道具吃了之后玛丽变大fire.cpp/h火焰敌人地图上的移动障碍master.cpp/h巡逻怪物在平台上往返移动pipe.cpp/h管道场景装饰与阻挡castle.cpp/h城堡关卡终点标志mypushbutton.cpp/h自定义按钮带图标和点击反馈的 QPushButtongamehelp.cpp/h帮助界面显示操作说明gamepause.cpp/h暂停界面游戏中暂停菜单这个拆分思路很适合新手读。每一个游戏对象一个类类内部只管自己的坐标、贴图、状态对象之间的交互统一交给 gamescene 去判断。如果你想改问号砖顶出什么东西只需要动 unknown.cpp 的相关逻辑不需要碰 mary 的代码。模块之间耦合很低这对课程设计或者二次开发来说是个很舒服的起点。2.3 从 main.cpp 到游戏场景事件循环背后的运行机制入口在 main.cpp。常见写法是先建 QApplication再实例化主窗口最后进入事件循环。主窗口上放几个 mypushbutton 按钮点击开始游戏后主窗口隐藏gamescene 显示游戏循环开始。// 入口代码标准 Qt 应用写法 #include QApplication #include mainwindow.h int main(int argc, char *argv[]) { QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }这段代码本身没什么难度重点是理解 Qt 的事件循环机制。a.exec()一旦启动程序就不再线性往下走而是等待用户操作产生事件再分发给对应的窗口和控件。游戏里的键盘按下、按钮点击、计时器触发全部走这套事件分发体系。gamescene 之所以能动起来靠的就是事件循环驱动定时器定时器每帧触发一次更新函数玛丽的位置和贴图才不断变化。主窗口里创建按钮并连接信号常见做法是这样// MainWindow 构造函数中创建开始按钮示意 #include mypushbutton.h #include gamescene.h MyPushButton *startBtn new MyPushButton(:/image/start.png); connect(startBtn, QPushButton::clicked, []() { this-hide(); GameScene *scene new GameScene(); scene-show(); });这里 MyPushButton 是项目里自定义的按钮类继承自 QPushButton构造参数传的是图片在 qrc 里的虚拟路径。connect把按钮的 clicked 信号和一段 lambda 槽函数连起来点击后主菜单隐藏游戏场景窗口创建并显示。new GameScene用的是栈上指针生命周期管理this-hide()保证主菜单窗口不销毁退出游戏后还能从暂停界面或结束界面回到菜单。3. 玛丽是怎么动的移动、跳跃、碰撞与敌人逻辑3.1 用状态机管理玛丽的动作平台跳跃游戏的核心问题是角色状态管理。玛丽的动作大体分四类站立、跑动、跳跃、死亡。如果不用状态机直接在键盘事件里写位移会出现跳起来还能在空中左右乱改速度、碰到敌人后还继续走路的逻辑混乱。源码包里的 mary 类文件不算大但这类项目的常见做法是给角色维护一个枚举状态每一帧根据状态决定贴图和处理逻辑。// 玛丽状态枚举常见做法示意 enum MaryState { Stand, // 站立 WalkLeft, // 向左跑 WalkRight, // 向右跑 Jump, // 跳跃 Die // 死亡 };每一帧的更新函数里先读键盘输入再根据当前状态计算速度变化最后更新坐标。水平方向和垂直方向是两套独立逻辑水平用左右键控制垂直由重力和跳跃初速度决定。松开方向键后角色不会立刻停住而是叠加一个摩擦力让速度逐渐衰减到零手感才自然不会像开关一样突然停。状态切换要留意一个细节玛丽跳到最高点时如果玩家一直按住跳跃键要不要再给一次上升经典超级玛丽没有二段跳所以状态机里从 Jump 回到 Stand 的唯一条件是落地检测而不是松开按键。很多 C 小游戏源码翻车在这里把跳跃做成了按住就无限上升或者按一下能跳两层楼高手感完全不对。3.2 矩形碰撞检测判断两个精灵是否重叠游戏对象的碰撞检测一般用 AABB 矩形碰撞也就是把每个对象当做一个轴对齐的矩形比较四条边的坐标是否重叠。Qt 的 QGraphicsItem 自带 collidesWithItem 可以判断但自己在坐标层面维护对象位置的项目通常直接写一个矩形相交函数逻辑简单又不受框架限制。// 矩形碰撞判断常见写法 bool checkCollision(const QRectF a, const QRectF b) { return a.left() b.right() a.right() b.left() a.top() b.bottom() a.bottom() b.top(); }四个条件分别比较左右边和上下边。a.left() b.right()表示 a 的左边界在 b 的右边界左边四个条件同时成立则两个矩形必然相交。这个函数对砖块、管道、蘑菇、敌人全部适用。唯一要注意的是别把整个贴图的矩形拿来碰撞图片的透明部分会导致碰撞框过大玛丽还没碰到砖块边缘就被弹回去视觉上像隔空被推非常难受。我一般会给每个对象单独保留一个碰撞矩形比贴图尺寸略小几个像素边缘留出视觉冗余这样玛丽能贴边滑过手感更宽松。源码包里 brick、pipe、castle 这些类只要看到里面有计算矩形边界的函数基本就是碰撞相关逻辑。3.3 问号砖触发和道具生成流程问号砖是这类游戏里最典型的交互对象。玛丽头部撞到问号砖下沿问号砖变普通砖同时弹出蘑菇或金币。这里的关键是碰撞方向判断玛丽从侧面碰到砖块时只做阻挡不能触发内容只有从下方撞上来、玛丽矩形底部位于砖块顶部之上才判定为顶砖。// 撞击方向判断的简化逻辑 bool hitFromBelow(const QRectF maryRect, const QRectF brickRect) { // 玛丽矩形底部的 y 值小于砖块矩形顶部的 y 值说明从下方撞入 return maryRect.bottom() brickRect.top() threshold; }蘑菇生成后自己带一个水平速度每帧更新位置碰到砖块边缘就反向。这里有个新手容易漏掉的细节生成出来的道具不能立刻参与碰撞否则玛丽还没站稳道具就卡在砖块内部出不来。常见做法是给道具设置一个短暂的出生等待时间比如 2 到 3 帧内不启用碰撞和移动先让道具原地冒头再开始弹跳。问号砖变普通砖后需要切换贴图这个可以在触发函数里直接替换 QPixmap同时把砖块状态标记为 used避免重复触发。3.4 敌人巡逻和玛丽死亡判定master.cpp 这个类对应巡逻怪物它和火焰一样属于动态障碍。实现上敌人每帧沿水平方向移动碰到砖块或管道边缘就掉头。判断掉头不看定时器而是检测前方是否有碰撞先假设下一帧位置如果下一帧位置和砖块相交就把移动方向取反。这样敌人不需要知道地图整体结构只在局部做碰撞试探代码简单而且不会穿墙。死亡判定有两种来源碰到敌人或火焰掉出地图边界。碰到敌人时还要区分伤害方式和踩踏判定经典超级玛丽里玛丽从上方踩到怪物怪物被消灭玛丽反弹从侧面碰到则玛丽受伤。这种上下判定用的还是碰撞方向和问号砖的触发逻辑类似只是反过来判断玛丽顶部是否位于敌人底部之上。掉出地图边界最简单每帧检查玛丽的 y 坐标是否超过场景高度减去安全值超了就进入 Die 状态画面切到死亡贴图然后显示 gameover 界面。4. 把工程编译跑起来Qt 环境配置与手感参数调整4.1 环境准备Qt 版本怎么选这个项目用的是 Qt Widgets 模块从 .pro 文件声明基本能判断它适配 Qt 5 或 Qt 6。下载源码包后建议优先用 Qt 5.15 或 Qt 6.2 以上的版本打开Qt 4 不能直接编译因为 widgets 模块在 Qt 5 才独立出来。安装 Qt 时编译器要选和安装包匹配的套件Windows 上用 Qt Creator 打开 super_mary.pro它会自动检测可用套件选择 Desktop Qt 5.15.x MinGW 64-bit 或 MSVC 2019 64-bit 均可。如果选了 MSVC 套件机器上还得装好 Visual Studio 的 C 开发组件否则报找不到编译器。macOS 和 Linux 上安装 Qt 之后命令行切到工程目录依次执行 qmake 和 make 就能编译。源码包里带了 .DS_Store 和 icon.icns看得出原作者在 macOS 上开发过但 Qt 工程本身跨平台Windows 和 Linux 编译没有任何问题。有一点要提前说解压路径别带中文和空格个别版本的 qmake 处理带空格的路径会抽风报一些莫名其妙的文件找不到错误。4.2 构建和运行的完整步骤拿到源码包后我习惯先跑一个最小验证确认环境没问题再改代码。以 Windows 命令行加 Qt 的 MinGW 套件为例# 命令行编译流程Windows 下 Qt 安装目录内的终端或 macOS/Linux 通用 cd supermary-main qmake super_mary.pro make -j4 ./super_maryQt Creator 里更简单直接打开 super_mary.pro点左下角绿色运行按钮。如果编译过程中报某个头文件找不到先检查是不是没有把对应 .cpp 加进 SOURCESqmake 每次构建前会自动重跑新增文件后要重新执行一次 qmake。运行后如果程序能启动但窗口没有背景图第一步看编译输出窗口有没有 Cannot find file ... 提示有就说明 qrc 路径写错了。资源路径统一以:/开头比如:/image/background1.jpg大小写必须和磁盘文件一致Windows 文件系统不区分大小写但 Qt 资源系统区分。4.3 关键参数重力、速度、跳跃高度放在哪玩过原版超级玛丽的人都懂手感是这个游戏的核心。跳跃高度、空中转身速度、落地缓冲这些数值没有标准答案纯粹是反复试出来的。源码包里的 mary.cpp 顶部通常就是一堆魔法数字重力加速度、水平速度、跳跃初速度、最大速度集中在文件开头方便微调。常见做法是用常量名把数值和含义对应起来// 角色手感参数常见取值区间可自行调整 const double kGravity 0.5; // 每帧重力加速度越大下落越快 const double kJumpSpeed -12.0; // 跳跃初速度负值向上 const double kMoveSpeed 3.0; // 水平移动速度 const double kMaxSpeed 6.0; // 水平最大速度 const double kFriction 0.8; // 地面摩擦力系数越小减速越快重力每帧叠加到纵向速度上速度再叠加到坐标上。跳跃初速度是负值因为屏幕坐标系 y 轴向下数值越小飞得越高。觉得玛丽跳太高把 kJumpSpeed 从 -12 改成 -10高度明显下降觉得跑起来太飘把 kFriction 从 0.8 调到 0.6地面减速更快角色显得更沉。调试手感时一次只改一个参数改完跑一次不要贪快同时动三个值否则改完说不清是哪个参数引起的变化后面想还原很难。5. 避坑手册资源丢失、编译报错、碰撞失灵5.1 图片全部不显示qrc 路径和大小写问题现象程序跑起来窗口空白背景、角色、按钮图标全都不显示某些平台上还出现黑块或默认图标。原因资源加载失败常见三种情况。一是图片文件没有在 res.qrc 里注册代码里引用了一个不存在的虚拟路径二是 qrc 文件里注册的路径和代码里的字符串不一致三是大小写问题Windows 文件系统不区分大小写但 Qt 资源系统区分写了:/Image/start.png而实际注册的是:/image/start.png照样加载失败。解决打开 res.qrc 对照检查把 code 里所有QPixmap(:/...)的字符串逐个比对。建议资源统一放 image 目录qrc 前缀固定为 /image代码里统一用:/image/文件名访问。养成这个习惯之后资源路径问题基本不会再出现。5.2 编译报错找不到 QWidgetQt 版本和套件不匹配现象编译到一半报QWidget: No such file or directory或者链接阶段报cannot find -lQt5Widgets。原因工程用了 widgets 模块但 .pro 里没写greaterThan(QT_MAJOR_VERSION, 4): QT widgets或者当前选中的 Qt 套件版本不对。Qt 6 之后部分模块重新组织Qt 5 的工程直接切到 Qt 6 也可能遇到头文件迁移问题比如某些#include Q...路径变了。解决确认 .pro 里 widgets 模块存在再回到 Qt Creator 的构建套件页面切换成和已安装 Qt 版本完全一致的套件。Qt 6 下遇到个别头文件找不到根据编译错误提示调整 include 路径。顺手在 .pro 里保留CONFIG c11lambda 表达式依赖 C11编译器标准太低会报语法错误。5.3 玛丽撞砖块会抖碰撞后位置复位方式不对现象玛丽站在地面上时角色上下抖动贴墙走时一卡一卡的像在砖块边缘反复抽搐。原因碰撞生效后需要把角色推出碰撞区域但推出方式不对。常见实现是检测到碰撞后直接把坐标设回上一帧位置如果上一帧位置和当前帧差距较大视觉上就是抖动另一类是水平和垂直方向在同一帧里同时回退互相干扰。解决碰撞响应按轴向拆开先做水平移动和水平碰撞检测再做垂直移动和垂直碰撞检测每个轴独立处理// 分轴碰撞处理示意 // 先移动 x 轴检查水平碰撞 m_x vx; if (checkCollision(...)) { m_x oldX; // 回退到碰撞前位置 vx 0; // 水平速度清零 } // 再移动 y 轴检查垂直碰撞 m_y vy; if (checkCollision(...)) { m_y oldY; vy 0; }分轴处理后水平方向和垂直方向的碰撞互不干扰抖动基本消失。源码包里如果玛丽在砖块边缘卡住多半就是这里处理得不够干净按这个思路改即可。5.4 键盘按了没反应焦点被按钮抢走现象游戏场景已经打开方向键却一点反应没有鼠标随便点击一下之后键盘又恢复正常。原因Qt 的键盘事件只发送给当前持有焦点的窗口或控件。从主菜单切换到游戏场景时如果场景窗口没有主动请求焦点或者界面里某个按钮仍然持有焦点键盘事件就传不到玛丽的按键处理函数里。解决在 gamescene 的构造函数里强制设置焦点策略并主动调用一次 setFocus// 构造函数中设置焦点策略示意 setFocusPolicy(Qt::StrongFocus); setFocus();Qt 里 QWidget 子类默认不接受键盘焦点必须显式设置 StrongFocus。如果是按钮触发的场景切换连接里再补一次 scene-setFocus() 更稳妥。遇到 Qt 游戏键盘失灵第一优先检查焦点策略这是出现频率最高的原因。5.5 中文乱码和打包夹带文件现象源码解压出来有一堆 .DS_Store 文件在 Windows 上编译正常但主菜单里的中文文字显示成乱码。原因.DS_Store 是 macOS Finder 自动生成的元数据文件不影响编译但很碍眼。中文乱码通常是源码文件编码和编译器默认编码不一致Qt 5 之后默认按 UTF-8 处理源文件如果源码是 GBK 保存的部分中文字符就被错误解析。解决用 Qt Creator 打开源码文件看底部状态栏显示的文件编码如果是 GBK 或 GB2312右键选择以 UTF-8 重新载入。.DS_Store 这种文件直接删掉对工程没有任何影响。另外 LICENSE 和 README.md 建议先读一遍源码包的授权方式和使用约束都在里面。6. 把这份源码改成你自己的游戏三个高频实操点6.1 调出你想要的手感数值调整建议先做减法再逐步加。以调轻快的玛丽为例先把重力降到 0.4让空中滞留时间变长再把跳跃初速度调大到 -13跳得更高。跑一圈试完一个参数再动下一个不要同时改三个值不然手感变了也不知道是谁的功劳。用 git 管理源码的话每次调整提交一次调崩了直接回退这是我吃了好几次亏之后养成的习惯。6.2 加一个简单的新关卡想体验扩展功能最直观的路径是给 gamescene 加第二张背景图和相关对象。先复制一张背景素材到 image 目录在 res.qrc 里注册然后调整游戏对象的坐标分布。坐标的常见做法是按地图格子计算格子尺寸固定为 32 像素新手指着网格摆放就能对齐砖块和管道。把管道整体右移 200 像素或者在一根管道前面多放三块砖角色跑动路线就变了游戏的节奏也随之改变。6.3 替换成自己的素材源码包里所有图片都是 PNG 和 JPG 格式想换成自己的素材直接替换同名文件即可。注意两点一是图片尺寸最好不要超过原图太多角色贴图过大会导致碰撞矩形不匹配视觉上角色还没碰到砖块就被挡下来二是文件名要严格保持原名因为代码里所有资源引用都写死了路径改名就得同步改 cpp 和 h 里的字符串犯不上。从那以后我每次拿到 C 游戏源码都会强制走一遍这套流程先看 .pro 确认模块依赖再跑一次最小编译验证环境最后才动手改代码。这个习惯救过我很多次希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑