ARTICLE DETAIL

资讯详情

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

Qt C++ FlappyBird课程设计源码解析:从QTimer游戏循环到碰撞检测

Qt C++ FlappyBird课程设计源码解析:从QTimer游戏循环到碰撞检测 简介基于 Qt 框架实现的 FlappyBird 小游戏完整源代码面向正在完成 C 课程设计或入门 Qt 图形界面开发的读者。项目实现了点击屏幕令小鸟上升、松开后下落、飞行途中躲避障碍物的核心玩法代码逻辑清晰并包含分数统计与游戏状态管理适合直接参照完成课设报告与答辩演示。资源包共含 80 个文件以 12 个 cpp 源文件、11 个头文件为主体另有 5 个 ui 界面文件、1 个 pro 工程文件与 1 个 qrc 资源文件47 张 png 图片完整覆盖了天空背景、小鸟动画与管道障碍等美术素材压缩后整体仅 263KB可直接导入 Qt Creator 编译运行。项目在结构上将主窗口、绘图场景、数据模型分层组织界面与逻辑分离便于理解 Qt 的事件处理、定时器驱动、碰撞检测和资源加载机制。目前已有 103 人学习过对想要快速搭建 Qt 小游戏框架、巩固 C 面向对象设计或完成课程设计的人来说是一份完整且可直接上手的参考资料。1. 基于 Qt 的 FlappyBird 课程设计一份能直接跑起来的 C 小游戏源码期末课程设计又撞上 FlappyBird 了网上搜“qt c FlappyBird 源代码”下载链接不少但多半是单文件 demo 或者缺资源的半成品解压之后编译报错能把自己绕晕。我这次拆的这份资源是一个完整的 Qt Widgets 工程.pro工程文件、MainWindow窗体、Graphics绘图逻辑、Models数据类、.ui界面描述、.qrc资源打包全都在解压后直接用 Qt Creator 打开就能构建不需要额外拼文件。它的玩法是点击屏幕让小鸟上升松手后自然下落撞到障碍物就结束——规则和你玩过的 FlappyBird 一致但代码结构和课程设计要求的“分层清晰”对得上号。适合三类人正在做 C 课程设计、需要一份能讲清楚原理的参考工程的同学刚学 Qt 没多久、想看看 QTimer 驱动游戏循环和 QPainter 绘图怎么配合的新手以及想快速改造一个完整小游戏、练手事件处理和碰撞检测的从业者。这篇笔记会把源码结构、Qt 版本选型、编译参数和常见报错一次讲透。2. 源码结构与工程组织FlappyBird.pro 到 Resources.qrc 的完整链路拿到压缩包解压后第一件事不是急着点编译而是把目录结构捋一遍。这份资源用的是 qmake 工程体系不是 CMake所以入口是FlappyBird.pro文件。Sources目录下是Main.cpp、MainWindow.cpp、Graphics.cppHeaders下对应MainWindow.h、Graphics.hForms里是MainWindow.uiResources里是Resources.qrc和Images图片目录。.gitignore和README.md也在说明作者是按正规工程习惯组织的。2.1 工程文件拆解qmake 的.pro到底写了什么.pro文件是 qmake 的构建脚本它决定了编译哪些源文件、链接哪些模块、生成什么目标。常见写法是这个套路QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET FlappyBird TEMPLATE app DEFINES QT_DEPRECATED_WARNINGS SOURCES \ Sources/main.cpp \ Sources/MainWindow.cpp \ Sources/Graphics.cpp \ Sources/Models/Bird.cpp HEADERS \ Headers/MainWindow.h \ Headers/Graphics.h \ Headers/Models/Bird.h FORMS \ Forms/MainWindow.ui RESOURCES \ Resources/Resources.qrc逻辑说明QT core gui是 Qt 基础模块greaterThan(QT_MAJOR_VERSION, 4): QT widgets是 Qt5 之后必须的写法否则QWidget、QPainter这类 GUI 类找不到定义。TARGET决定生成的可执行文件名TEMPLATE app表示生成应用程序而不是库。SOURCES、HEADERS、FORMS、RESOURCES分别登记源码、头文件、UI 文件和资源文件。参数说明如果你的项目里新增了一个Pipe.cpp文件必须同步加进SOURCES否则编译能过但链接阶段会报“undefined reference”。这是 Qt 新手最常见的翻车点。另外DEFINES QT_DEPRECATED_WARNINGS只是编译期警告不影响运行想精简可以删掉。2.2 UI 文件与资源系统的配合方式MainWindow.ui是 Qt Designer 生成的 XML 描述文件描述窗口尺寸、按钮位置、布局方式。这个项目里 UI 文件承担的是主窗口骨架真正的游戏画面是后续用QPainter在Graphics类里绘制的。理解这个分工很重要.ui定的是“壳”Graphics.cpp画的是“肉”。Resources.qrc是资源打包文件把所有图片资源编译进可执行文件RCC qresource prefix/ fileImages/bird.png/file fileImages/pipe.png/file fileImages/background.png/file /qresource /RCC逻辑说明prefix/表示资源在根路径下代码里用:/Images/bird.png就能访问。这种方式的优势是程序发布时不用单独带图片目录所有资源都在 exe 里对课程设计答辩演示特别省心——拷到哪台机器都能跑。参数说明图片路径是相对.qrc文件所在目录的。Resources.qrc放在Resources文件夹里所以图片路径写成Images/bird.png而不是Resources/Images/bird.png。如果编译后图片加载不出来先检查这里。这份工程的文件组织方式对课程设计答辩很友好。老师问“你的项目是怎么组织的”你可以直接指着目录结构讲Models放数据类、Graphics放绘制逻辑、Forms放界面描述。分层意识在 C 课程设计里是加分项这份资源已经把骨架搭好了。3. 游戏循环与碰撞检测QTimer、事件处理与 QPainter 绘制的配合FlappyBird 的核心机制不复杂小鸟受重力影响持续下落点击屏幕后获得一个向上的瞬时速度障碍物从右向左匀速移动撞到障碍物或地面就结束。难点在于“游戏循环”如何用 Qt 的事件模型实现而不是像控制台程序那样用while(true)死循环。3.1 主循环的搭建QTimer 就是你的游戏心跳Qt 是事件驱动框架主线程不能阻塞所以游戏循环要用QTimer定时触发刷新// MainWindow.cpp MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { // 初始化游戏对象 bird new Bird(); scene new Graphics(this); // 驱动循环的定时器每 16ms 触发一次约 60 FPS timer new QTimer(this); connect(timer, QTimer::timeout, this, MainWindow::updateGame); timer-start(16); setCentralWidget(scene); } void MainWindow::updateGame() { bird-applyGravity(); // 小鸟受重力下落 bird-flapIfNeeded(); // 如果有点击事件给小鸟一个上升冲量 scene-update(); // 触发重绘 }逻辑说明QTimer的timeout信号每 16ms 发一次每次触发就更新游戏状态然后让scene重绘。这样游戏逻辑和绘制逻辑是分离的——updateGame()里改的是数据paintEvent()里根据数据画界面。数据与显示分离是 Qt 绘图的基本功也是这份工程最值得抄的写法。参数说明timer-start(16)的 16ms 对应约 60FPS。想调难度改的是bird-applyGravity()里的重力常量而不是改定时器频率——定时器频率受系统调度影响不精确不适合做难度参数。3.2 点击事件与小鸟物理模型鼠标点击的处理是重写mousePressEvent这是一个事件回调不是轮询void MainWindow::mousePressEvent(QMouseEvent *event) { if (event-button() Qt::LeftButton) { bird-flap(); } QMainWindow::mousePressEvent(event); }// Models/Bird.cpp 中体现了简化物理模型 void Bird::applyGravity() { vy gravity; // 速度累加重力加速度 y vy; // 位置累加速度 } void Bird::flap() { vy -jumpVelocity; // 点击瞬间速度设为向上的负值 }逻辑说明物理模型用的是最简单的欧拉积分——vy gravity让速度每帧增加一个固定值模拟重力加速度y vy让位置随速度变化。点击时直接把vy设为负值也就是给一个向上的瞬时的冲量。这个模型足够还原 FlappyBird 的手感又不会引入复杂物理公式。参数说明gravity和jumpVelocity是两个关键调参变量。gravity太大小鸟掉落像石头jumpVelocity太大点击一次飞出去老远。经验值范围是gravity在0.3~0.5、jumpVelocity在-8~-6具体数值取决于你的窗口高度和定时器频率。3.3 障碍物生成与碰撞判定障碍物是成对出现的上下管道中间留一个缝隙。管道从右侧向左移动移出屏幕后回收复用void Graphics::updatePipes() { // 每根管道向左移动固定步长 for (Pipe pipe : pipes) { pipe.x - pipeSpeed; } // 移出左侧屏幕的管道移除右侧生成新管道 if (!pipes.isEmpty() pipes.first().x pipeWidth 0) { pipes.removeFirst(); } if (pipes.isEmpty() || pipes.last().x sceneWidth - pipeSpacing) { createPipe(); } } bool Graphics::checkCollision(const Bird bird) { QRect birdRect bird.rect(); // 超出上下边界直接判死 if (birdRect.top() 0 || birdRect.bottom() groundHeight) { return true; } // 与每根管道做矩形相交检测 for (const Pipe pipe : pipes) { QRect topPipeRect(pipe.x, 0, pipeWidth, pipe.gapY - gapHeight / 2); QRect bottomPipeRect(pipe.x, pipe.gapY gapHeight / 2, pipeWidth, groundHeight - pipe.gapY); if (birdRect.intersects(topPipeRect) || birdRect.intersects(bottomPipeRect)) { return true; } } return false; }逻辑说明碰撞检测用的是 Qt 自带的QRect::intersects把小鸟和管道都抽象成矩形做相交判断。上下边界的判断要放在矩形碰撞之前——如果小鸟已经飞出屏幕矩形碰撞的结果没有意义。gapY是管道缝隙的中心位置gapHeight是缝隙大小这两个参数直接决定游戏难度。参数说明缝隙宽度gapHeight建议在120~160像素之间小于 100 基本玩不过去大于 200 又太简单。管道间距pipeSpacing控制在250~350像素太密没有反应时间太疏节奏太慢。这里有个容易被忽略的细节QTimer驱动的游戏循环天然存在帧率不稳定的问题。如果系统繁忙timeout间隔会变长小鸟的物理模拟就会变慢。严谨的做法是记录两次timeout之间的真实时间差dt用dt修正位移量。但这个项目是课程设计固定步长已经够用答辩时能说出这个改进方向反而是加分项。4. 编译配置与运行环境从 Qt 5.15 到 Qt 6 的兼容性处理源码能跑起来才算数。这一章解决“拿到工程后怎么编译”的问题包括 Qt 版本选择、编译器套件配置、资源路径检查三个环节。4.1 Qt 版本与编译器套件的选择这份工程的.pro文件是按 Qt 5 的写法组织的Windows 下推荐用 Qt 5.15.2 LTS 版本这是一个非常稳定的长期支持版本。官方安装包通常包含多个编译器套件常见的是MSVC 2019 64-bit和MinGW 64-bit两套套件编译器适用场景注意点MSVC 2019 64-bitcl.exeWindows 原生开发性能好需要 Visual Studio 2019/2022 的 C 组件MinGW 64-bitg轻量级不需要 VS生成的 exe 依赖 MinGW 运行时库选择原则很简单你机器上装了 Visual Studio 就选 MSVC 套件没装就选 MinGW 套件。两者生成的 exe 不能混用——MSVC 编译的程序不能在 MinGW 环境跑反之亦然。4.2 打开与构建工程的完整步骤命令行构建和 Qt Creator 图形界面构建是两种常见做法。Qt Creator 的操作流程是打开 Qt Creator选择File-Open File or Project选中FlappyBird.pro选择配置好的 Qt 版本和编译器套件点击Configure Project点击左下角的绿色三角按钮构建并运行命令行构建更直接适合熟悉终端操作的人mkdir build cd build qmake ..\FlappyBird.pro mingw32-make参数说明qmake会根据.pro文件生成对应平台的 Makefilemingw32-make执行编译和链接。如果你的环境是 MSVC最后一步换成nmake。构建成功后在当前目录下会生成FlappyBird.exe。逻辑说明qmake是 Qt 特有的构建工具它和 CMake 是两种不同的构建体系。你觉得 CMake 更通用但这份工程就是 qmake 的直接用 qmake 构建最省事不用手动改写 CMakeLists.txt。4.3 高版本 Qt 的兼容性调整如果你机器上装的是 Qt 6直接打开会报一堆错误。主要原因是 Qt 6 里QPainter的高精度坐标 API 变了还有部分类的位置调整过。常见的兼容性修改是这两处// Qt 5 写法工程原始代码 painter-drawText(birdPos, QString(Score: %1).arg(score)); // Qt 6 兼容写法 painter-drawText(birdPos.toPoint(), QString(Score: %1).arg(score));逻辑说明Qt 5 里QPainter::drawText的重载函数接受QPointF但 Qt 6 收紧了接口要求显式传入QPoint或QRect。如果你的编译报错指向drawText的重载不匹配加上.toPoint()转换就行。参数说明另一个 Qt 6 的坑是QRandomGenerator替代了旧的qrand()。如果你在源码里看到qrand()报错改成QRandomGenerator::global()-bounded(max)取值。4.4 资源加载失败的排查路径如果程序能编译通过但窗口里图片不显示问题大概率出在.qrc文件上。检查顺序三步走先确认.qrc里的路径是相对路径而不是绝对路径再确认图片文件名的大小写和实际文件一致Windows 不区分大小写但 Qt 资源的索引是敏感的最后看图片格式.png和.jpg都支持但某些.bmp的透明通道处理在 Qt 里表现不稳定。5. 编译与运行避坑指南五个高频报错的诊断与解决这条路我走过不止一次。以下五条是拿到这份源码后最可能踩中的坑每条都是真实场景按“现象 → 原因 → 解决”写清楚。5.1 现象:-1: error: dependent ..\..\qt\5.15.2\msvc2019_64\include\QtWidgets does not exist原因这个报错非常典型——Qt Creator 里选的编译器套件和你安装的 Qt 库不匹配。报错信息里的msvc2019_64路径说明当前套件是 MSVC 编译的 Qt 库但你用的编译器可能是 MinGW或者反过来。Qt 的库是分编译器编译的MSVC 版的库只能在 MSVC 的环境下链接。解决在 Qt Creator 的Tools-Options-Kits里检查当前 Kit 的“Compiler”和“Qt version”是否匹配。MSVC 套件配 MSVC 版 QtMinGW 套件配 MinGW 版 Qt不要跨编译器混用。5.2 现象undefined reference to MainWindow::MainWindow(QWidget *)链接失败原因.pro文件里的SOURCES没包含MainWindow.cpp或者文件名拼写有误。qmake 是按.pro里登记的源文件列表去编译的漏掉一个就会在链接阶段找不到符号。解决打开.pro文件确认SOURCES 那几行包含所有.cpp文件如果你新增了Pipe.cpp之类的文件手动补上这一行重新执行 qmakeQt Creator 里右键工程根目录选Run qmake再构建。5.3 现象窗口显示出来但游戏画面完全空白原因游戏窗口空白通常是两个原因叠加——QTimer没启动或者paintEvent里QPainter没调用begin/end配对。很多初学者实现绘制时写了painter.begin(this)但忘了painter.end()或者反过来导致绘制状态错乱。解决检查MainWindow构造函数里timer-start(16)是否执行到再看Graphics::paintEvent里QPainter painter(this)的构造方式是否正确。这个项目用的是QPainter painter(this)构造函数自动绑定画布的方式不需要手动begin/end。5.4 现象图片加载不出来QPixmap is null原因资源路径写错了。直接写:/bird.png但.qrc里的实际路径是:/Images/bird.png少了一层目录就找不到。另一种常见情况是.qrc文件没有在.pro里登记资源根本没有被编译进去。解决用RCC结构检查.qrc的prefix和file路径。如果prefix是/那么:/Images/bird.png就是正确的访问路径。可以先用绝对路径排错确认图片文件本身没问题后再回头查.qrc配置。5.5 现象高分辨率屏上游戏窗口显示异常字体重影原因Qt 5.15 之前的高 DPI 缩放需要手动启用如果你的程序没设置Qt::AA_EnableHighDpiScaling或没有在.pro里加QT widgets的HIGHDPI处理在 Windows 缩放比例超过 100% 时就会出现模糊或错位。解决在main.cpp的QApplication构造之前加两行QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);逻辑说明这两行让 Qt 在启动时自动适配系统缩放比例AA_EnableHighDpiScaling处理逻辑坐标到物理坐标的映射AA_UseHighDpiPixmaps让图片资源在缩放时保持清晰。注意这两行必须在QApplication实例创建之前调用否则不生效。6. 改造与发布把课程设计做成能答辩的完整项目源码能跑只是第一步课程设计想要拿高分得让老师看到“我不仅做出来了还知道怎么改进”。6.1 计分系统与游戏状态机的实现思路原版 FlappyBird 每次穿过一对管道得一分。当前工程的核心循环在updateGame()里改造计分的关键是检测“小鸟穿过了管道的判定线”这个事件而不是每帧加分。常见做法是给每根管道加一个scored标志位当小鸟的x坐标超过管道中心且scored false时分数加一scored置为truevoid Graphics::updateScore(Bird bird, int score) { for (Pipe pipe : pipes) { if (!pipe.scored bird.x() pipe.x pipeWidth / 2) { score; pipe.scored true; } } }参数说明pipe.scored是关键标志位防止同一根管道被重复计分。按这个思路扩展你还可以在Bird类里加state枚举Ready、Flying、Dead用状态机控制游戏开始、运行、结束三个阶段——这是课程设计答辩里老师最喜欢听到的“软件工程设计思想”。6.2 音效与 QSoundEffect 的接入FlappyBird 的反馈感很大程度来自音效。Qt 里接入音效用QSoundEffect最轻量QSoundEffect *flapSound new QSoundEffect(this); flapSound-setSource(QUrl(qrc:/Sounds/flap.wav)); flapSound-setVolume(0.5f); flapSound-play();逻辑说明QSoundEffect适合播放短音效它底层是异步播放不会阻塞 UI 线程。注意音频文件也要加进.qrc才能用qrc:路径访问。如果不想引入音效文件也可以用一个更取巧的方案用QApplication::beep()或QApplication::alert()做系统提示音但效果粗糙不推荐用于正式项目。6.3 发布前必须检查的清单答辩演示往往是用老师的电脑或投影机环境不可控。每次发布前我习惯强制走一遍这样的检查流程顺序不能乱构建模式切到ReleaseQt Creator 左下角的“构建”按钮旁边可以选择 Debug/ReleaseRelease 版本体积小、运行快不依赖调试库用windeployqt工具部署运行库到 exe 所在目录命令是windeployqt FlappyBird.exe它会自动把需要的 Qt DLL 拷贝过来防止目标机器没装 Qt确认资源和图片都打包进去了——windeployqt只处理 DLL不处理你的图片所以.qrc必须是编译进来的而不是外部读取换一台没有装 Qt 的干净机器跑一遍验证部署是否完整如果闪退优先看缺哪个 DLL用Dependencies工具检查部署这步是课程设计答辩翻车的重灾区。很多同学在开发机上跑得好好的一换电脑就启动报“找不到 Qt5Core.dll”就是因为没做 windeployqt。从那以后我每次替别人看 Qt 项目第一件事就是检查 exe 同级目录的 DLL 是否齐整再决定要不要上机测试。这份 FlappyBird 源码本身没这个问题但你的改造版本很可能遇到希望帮到你。本文还有配套的精品资源点击获取
返回列表