简介一份基于QTC开发的flappyBird游戏完整源码面向毕业设计、课程设计与项目开发场景适合已有C基础的在校学生或开发者参考使用。项目代码经过严格测试打开工程即可编译运行也便于在现有逻辑上继续延展附带的README与工程文件让环境配置更省心。压缩包共27个文件大小3.38MB其中5个cpp源文件与4个头文件构成核心代码8张png图片和mp3、wav音频负责视觉与音效另外还包含pro工程文件、qrc资源清单以及exe可执行程序编译前无需再手工配置Qt资源与依赖。已有187人浏览学习。借助此项目可练习QT窗口与事件循环、碰撞检测、自定义绘图、音乐播放等常见开发技巧从主程序到界面、障碍物、道路等模块划分清楚配套目录结构易于按需查阅和二次开发适合作为游戏入门或课设作品的基础模板。1. 基于QTC开发的flappyBird游戏源码拆解与课程设计落地实践对很多计算机专业的学生来说课程设计和毕业设计最头疼的不是“会不会写代码”而是“代码从哪来、拿来能不能跑、跑了能不能讲清楚”。拿 flappyBird 这个小游戏来说它看着简单但涉及 Qt 的事件循环、定时器驱动、碰撞检测和 2D 绘图这几个核心机制恰好是 Qt 课程设计里最常考的能力点。这份基于 QTC 开发的 flappyBird 游戏源码把整个游戏从无到有拆成了 roadItem、pipeItem、widget 三个核心类麻雀虽小五脏俱全。适合三类人一是想快速交一份能演示的 Qt 课设的在校生二是想搞懂 Qt 2D 游戏最小架构的开发者三是想把时间花在改功能上、而不是从零画水管和地面的人。它不依赖任何第三方库纯 Qt Widgets 实现拿到手可以直接编译运行。下面我把这个项目的结构、原理和踩坑点一条条拆开讲。2. 项目结构与 Qt 游戏最小架构先看懂 roadItem 和 pipeItem 的设计2.1 文件清单的功能映射每个文件在游戏循环里的位置解压源码包后根目录下是一套完整的 Qt qmake 工程。用 Qt Creator 打开flappyBird.pro就能直接构建。文件结构不长但每个文件都对应一个明确的职责先把映射关系理清文件职责对应游戏机制widget.h / widget.cpp主窗口与游戏循环控制器定时器驱动、按键处理、游戏状态切换item.h / item.cpp所有可移动物体的基类位置更新、碰撞矩形roadItem.h / roadItem.cpp地面移动组件无限滚动地面pipeItem.h / pipeItem.cpp上下水管组件障碍物生成与移动main.cpp程序入口Qt 事件循环启动src.qrc资源文件索引图片和音频资源打包进二进制image / sound资源目录素材存放位置这里的核心设计思路是连续移动物体统一继承item基类。基类里只放位置、速度和碰撞矩形派生类只负责自己的绘制外观和越界处理。这种写法在 Qt 游戏里非常实用后续想加金币、加云朵直接继承item写一个子类就行不用动主循环代码。2.2 item 基类位置、速度与碰撞矩形的约定item.h是整个项目里最值得抄的代码之一因为它把 Qt 2D 游戏中“每个帧要做什么”抽象成了三个接口。看核心成员// item.h 核心结构 class item { public: item(); virtual ~item(); // 每帧更新子类重写此方法实现各自移动逻辑 virtual void update() 0; // 获取碰撞矩形用于与小鸟、地面进行相交检测 QRectF getRect() const { return rect; } protected: QRectF rect; // 碰撞矩形位置 尺寸 QPointF velocity; // 速度向量像素/帧 bool active; // 是否参与游戏逻辑 };这段代码里最关键的两个设计决策碰撞矩形rect和绘制位置共用同一份数据。很多新手会把“画在哪”和“碰在哪”分开处理结果就是小鸟看着撞上了水管判定却没触发或者反过来。这里强制让碰撞矩形就是绘制区域从根上规避了这类问题。纯虚函数update()。Qt 的定时器每次超时后主循环只需要遍历所有active为真的 item 调用update()不必关心每个 item 具体是什么类型。这就是多态在游戏循环里的经典用法。QRectF是 Qt 自带的高精度矩形类型用浮点数存储坐标和宽高。在 60 FPS 的游戏循环里浮点坐标能避免像素累积误差——如果用QRect的整数坐标每帧移动 1.5 像素时会被截断成 1 像素长期运行后物体的实际位置会偏离预期。2.3 roadItem 和 pipeItem两种不同节奏的横移实现roadItem是地面滚动实现思路是两张图轮流衔接。地面图片宽于屏幕一次画两张当第一张完全移出屏幕左侧时把它重新拼接到第二张右侧。代码逻辑如下// roadItem.cpp 地面无限滚动核心 void roadItem::update() { // 地面向左移动每帧移动速度固定 rect.moveLeft(rect.left() - speed); // 当当前这张地面的右边界已经完全移出屏幕 if (rect.right() 0) { // 把这张地面拼接到另一张的右侧形成无缝循环 rect.moveLeft(rect.left() rect.width() * 2); } }pipeItem的横移逻辑和roadItem类似但多了两个关键差异第一水管有成对的上下两部分中间留出一个缺口让小鸟飞过缺口位置是随机生成的但随机范围被限制在屏幕高度的 25%~75% 之间避免缺口太靠上或太靠下导致玩家完全无法应对第二水管移出屏幕后不会像地面那样循环拼接而是直接标记为active false等待内存回收或复用。水管缺口的间距是一个需要调的参数。代码里默认的缺口高度在 150~180 像素之间这个值对应 800x600 的窗口分辨率。如果你把窗口改大了缺口不跟着调游戏难度会骤变——缺口太宽没挑战性太窄玩家过不去。后面我会在参数调整章节细说。3. 游戏主循环与渲染机制widget.cpp 里的定时器、按键响应和碰撞处理3.1 QTimer 驱动游戏循环为什么不用 while(1)侧面看widget.cpp 是整个游戏的“心脏”。很多从 Unity 或 Cocos2d 转过来的同学会习惯性地想写while(1) { update(); render(); }但在 Qt 里这是大忌——它会阻塞 Qt 的事件循环窗口直接卡死无响应。项目的正确做法是用QTimer把更新和重绘挂进事件循环// widget.cpp 构造与游戏循环初始化 widget::widget(QWidget *parent) : QWidget(parent) { // 其他初始化省略... timer new QTimer(this); // 连接定时器超时信号到更新槽函数 connect(timer, QTimer::timeout, this, widget::gameLoop); // 每隔 16ms 触发一次约等于 60 FPS timer-start(16); } void widget::gameLoop() { // 只有运行中的游戏才驱动逻辑更新 if (gameState GAME_RUNNING) { // 遍历所有活动 item更新位置 for (item *it : items) { if (it-isActive()) { it-update(); } } // 碰撞检测 checkCollisions(); // 请求 Qt 重绘整个窗口 update(); } }这里的timer-start(16)是一个经验参数。16 毫秒约等于 60 FPS是绝大多数显示器的基础刷新率。如果你的机器是 120Hz 或 144Hz 的屏幕60 FPS 也仍然够用——这个游戏本身逻辑简单60 FPS 下画面足够丝滑把定时器改成 8 毫秒反而会在屏幕刷新时产生撕裂感收益不大。update()这个调用值得多说一句。很多新手在这里会直接调用repaint()这不是不能用但repaint()是同步强制重绘会阻塞当前线程直到绘制完成update()是异步的Qt 会把多个重绘请求合并到下一次事件循环中统一处理性能好得多。游戏循环里一律用update()这是 Qt 优化的惯例。3.2 按键响应与小鸟的物理模型非对称跳跃的数值讲究小鸟的移动不是简单地“按一下上升松手下降”而是用一个速度向量来模拟重力。代码的实现思路是// widget.cpp 小鸟跳跃与重力 void widget::keyPressEvent(QKeyEvent *event) { if (event-key() Qt::Key_Space) { if (gameState GAME_READY) { // 准备状态下按空格开始游戏 gameState GAME_RUNNING; } else if (gameState GAME_RUNNING) { // 运行状态下给小鸟一个向上的瞬时速度 birdVelocity jumpSpeed; // jumpSpeed 一般是负值 } else if (gameState GAME_OVER) { // 结束状态下重启 resetGame(); } } } void widget::gameLoop() { // 每帧给小鸟施加向下的重力加速度 birdVelocity gravity; // gravity 0.5 像素/帧² birdRect.moveTop(birdRect.top() birdVelocity); // 边界处理碰地面或顶天花板 if (birdRect.top() 0) { birdRect.moveTop(0); birdVelocity 0; } }这套物理模型的参数对游戏手感影响极大。项目的默认参数大致是jumpSpeed -8负值表示向上gravity 0.5像素/帧²。用这个组合小鸟每次点击后会有明显的上升抛物线下落过程中加速度递增符合“越掉越快”的直觉。这里有个细节坑如果jumpSpeed的绝对值太小比如-5小鸟会显得“压手”玩家会觉得按了没反应如果太大比如-12小鸟每次跳跃上升幅度过大玩家没法精细控制高度。调到-8左右是一个能兼顾操控感和视觉表现的中间值。这个参数背后的数学逻辑是每次跳跃的总上升距离约等于jumpSpeed² / (2 * gravity)算下来大约是 64 像素正好是水管缺口高度的一半左右保证玩家一次跳跃能跨越半个缺口。另外可以看到keyPressEvent里对游戏状态做了三重判断——准备、运行、结束分别响应不同的操作。这是 Qt 游戏里最朴素也最好用的状态机用枚举变量标识状态在不同状态下对同一事件做不同处理比用一堆布尔量组合判定清晰得多。3.3 碰撞检测的边界陷阱QRectF::intersects 的误区碰撞检测是 flappyBird 里最容错的一个环节。项目里的实现大致是// widget.cpp 碰撞检测 void widget::checkCollisions() { // 1. 小鸟与地面碰撞 if (birdRect.intersects(roadRect)) { gameOver(); return; } // 2. 小鸟与上下水管碰撞 for (pipeItem *pipe : pipes) { // 只检测 active 的水管节省性能 if (!pipe-isActive()) continue; QRectF topPipeRect pipe-getTopPipeRect(); QRectF bottomPipeRect pipe-getBottomPipeRect(); if (birdRect.intersects(topPipeRect) || birdRect.intersects(bottomPipeRect)) { gameOver(); return; } } }看着简单但这里有一个常见的视觉与逻辑不同步坑绘制水管时用的图片通常四周带透明边距如果碰撞矩形直接等于整个图片矩形小鸟明明看着擦到水管边缘一点点却被判定为撞上玩家会非常沮丧。反之如果把碰撞矩形缩小到比可见区域还小又会出现“穿模”的错觉。项目的处理方式是碰撞矩形比图片实际可见区域每边缩进 4~6 像素。这个值不能太大太大会出现明显的“都撞上了怎么还没死”的作弊感。以 34x240 的水管图片为例碰撞矩形实际采用约 26x230 的样子。这种像素级的微调是让游戏“手感对”的关键细节——玩过原版 flappyBird 的人会知道原版也有类似的碰撞缩进。3.4 绘制顺序与绘制性能paintEvent 的调用约定paintEvent里画的东西是有严格顺序的先背景再水管再地面最后小鸟。Qt 的绘制是按调用顺序层层覆盖的这个顺序不能反。代码大概长这样// widget.cpp 绘制函数 void widget::paintEvent(QPaintEvent *) { QPainter painter(this); // 1. 背景图铺满整个窗口 painter.drawPixmap(0, 0, width(), height(), backgroundPixmap); // 2. 双缓冲先绘制到离屏 pixmap再一次性贴上来 QPixmap bufferPixmap(size()); QPainter bufferPainter(bufferPixmap); bufferPainter.fillRect(rect(), Qt::transparent); // 画水管 for (pipeItem *pipe : pipes) { if (!pipe-isActive()) continue; bufferPainter.drawPixmap(pipe-getTopPipeRect(), pipe-getTopPipePixmap()); bufferPainter.drawPixmap(pipe-getBottomPipeRect(), pipe-getBottomPipePixmap()); } // 画地面 bufferPainter.drawPixmap(roadRect, roadPixmap); // 画小鸟按当前旋转角度 painter.save(); painter.translate(birdRect.center()); painter.rotate(birdRotationAngle); painter.translate(-birdRect.center()); painter.drawPixmap(birdRect, birdPixmap); painter.restore(); // 3. 把离屏内容绘制到窗口 painter.drawPixmap(0, 0, bufferPixmap); }这里用了两级绘制策略先用bufferPixmap在离屏画好所有内容再一次drawPixmap上屏。这种做法叫双缓冲目的是避免画面闪烁——如果直接在painter上逐个绘制中间过程用户能看到未完成的画面高速运动下就是明显的闪烁和撕裂。小鸟的旋转是一个值得学的技巧。painter.translate(birdRect.center())把坐标原点移到小鸟中心rotate(birdRotationAngle)按角度旋转画布再translate(-birdRect.center())把原点移回来。这样绘制出来的小鸟会绕自身中心旋转而不是绕屏幕原点旋转。旋转角度通常跟小鸟的垂直速度挂钩上升时抬头下落时低头速度越快角度越大这样画面会非常自然。4. 从源码到可运行程序编译配置、素材路径与 Qt 版本兼容性4.1 用 Qt Creator 打开并运行项目qmake 工程的完整操作路径拿到源码后第一步不是直接双击flappyBird.pro而是确认 Qt 环境。项目是基于 Qt Widgets 的 qmake 工程需要 Qt 5.12 或更高版本Qt 6 也能编译但部分 API 有微小差异后面说。操作步骤# 1. 解压压缩包Windows 上用解压工具即可 unzip QT开发的flappyBird游戏.zip # 2. 进入项目目录 cd flappyBird-main # 3. 查看 Qt 版本确保 qmake 在 PATH 中 qmake --version # 4. 生成 Makefileqmake 会读取 flappyBird.pro qmake flappyBird.pro # 5. 编译Windows 上若用 MinGW 套件用 mingw32-make make -j4如果是在 Qt Creator 里操作更简单文件 - 打开文件或项目 - 选择 flappyBird.pro - 选择已安装的 Qt Kit - 运行。qmake 会自动处理依赖关系和资源文件src.qrc的编译。这里有一个新手最容易翻车的点flappyBird.pro文件里通常写有QT widgets这一行。如果你的 Qt 安装时没勾选 Widgets 组件默认是勾选的但有些精简安装会去掉编译时会报Project ERROR: Unknown module(s) in QT: widgets。解决办法是重新安装 Qt 并勾选上 Qt Widgets 组件或者在.pro文件中删掉这一行改用 Qt Quick但后者意味着整套代码都要换写法不建议。4.2 src.qrc 资源系统为什么图标和图片要打包进二进制项目里的图片和音频文件不是直接放在可执行文件旁边的而是通过src.qrc文件组织起来编译时嵌入到二进制里。这样做的好处是发布时不用带一堆零散素材文件只要一个 exe 就能跑。看src.qrc的大致结构!-- src.qrc 资源文件结构 -- RCC qresource prefix/ fileimage/bird.png/file fileimage/background.png/file fileimage/pipe_up.png/file fileimage/pipe_down.png/file fileimage/ground.png/file filesound/die.wav/file filesound/hit.wav/file filesound/point.wav/file filesound/swooshing.wav/file filesound/wing.wav/file /qresource /RCC加载资源的方式和普通文件路径不同代码里用的是资源路径语法// 加载声音资源 QSoundEffect *wingSound new QSoundEffect(this); wingSound-setSource(QUrl(qrc:/sound/wing.wav));这里的qrc:/前缀是 Qt 资源系统的标准访问协议。如果资源文件里路径写错了运行时会一直黑屏或没有声音但编译不会报错——因为资源是在运行时按路径索引的编译器检查不到字符串路径是否正确。这个坑非常隐蔽排查时优先看资源路径有没有拼写错误。4.3 Qt 6 兼容性哪些 API 变了如果你的开发环境是 Qt 6项目可能需要小幅改动才能编译通过。最常见的两个差异第一QSound类在 Qt 6 中被移除了如果原项目用的是QSound::play()需要改成QSoundEffect用法上面代码已经演示或QMediaPlayer。QSoundEffect适合短音效延迟低播放wav文件效果不错。第二QMouseEvent的pos()方法在 Qt 6 中改成了position().toPoint()。如果项目里有鼠标点击检测的逻辑需要相应修改。这份源码里主要用的是键盘事件鼠标事件如果涉及也就一个位置获取的问题改动量不大。Qt 5.15.2 是最稳妥的选择——它是 Qt 5 系列的最后一个长期支持版本兼容性最好网上关于 Qt 5 的中文资料也最多。如果你正在用 Visual Studio 2026 配 Qt 6.11编译时遇到莫名奇妙的报错建议先退回 Qt 5.15.2 验证一下是不是版本差异导致的。5. 避坑与常见问题排查编译报错、运行黑屏、碰撞判定失常怎么办5.1 编译时报错“cannot mix incompatible Qt library”现象编译链接时弹出类似Fatal: cannot mix incompatible Qt library (version ex50601) with this library的报错。原因这个报错的意思是链接器发现不同的库文件来自不同的 Qt 版本。最常见的情况是系统里装了两个 Qt比如 Qt 5.12 和 Qt 5.15或者 Qt 库路径和编译器路径在 PATH 环境变量里冲突。Qt Creator 自动选择的 Kit 和项目实际用的 qmake 来自不同安装目录时也会触发这个错误。解决在 Qt Creator 中进入工具 - 选项 - Kits确保选中一个“桌面”套件然后构建项目。如果是在命令行编译检查qmake --version输出的路径是否唯一必要时用set PATHC:\Qt\5.15.2\mingw73_64\bin;%PATH%这样的方式把优先级理顺。我自己的习惯是每次开新项目先跑一遍qmake --version确认当前环境这个习惯帮我排掉过不少莫名其妙的链接错误。5.2 运行后窗口黑屏、看不到任何内容现象程序能编译但运行后窗口一片黑背景、小鸟、地面都看不到。原因黑屏有两个常见根源。第一素材加载失败——图片路径写错了或者src.qrc里的文件路径与实际文件结构不一致运行时加载 pixmap 返回空对象绘制出来全是透明内容。第二paintEvent里没有被正确调用——比如忘了在构造里设置setAttribute(Qt::WA_OpaquePaintEvent)或者事件循环被阻塞。解决先检查素材加载代码打印QFile::exists(:/image/background.png)看资源是否存在。然后在paintEvent第一行加一句qDebug() paint如果能打印但画面仍为空说明是素材问题如果paint都不打印说明窗口没接收到刷新请求。另外确认src.qrc文件在 Qt Creator 的“添加新文件”流程中是否真的被标注为“资源文件”——有时候文件虽然加进了文件夹但没写进.pro文件的RESOURCES src.qrc这行资源就编译不进去。5.3 小鸟飞过水管缺口时还是判定碰撞现象屏幕上小鸟和水管缺口明显有重叠但游戏没有结束或者小鸟视觉上完全飞过去了却被判定碰撞。原因这类问题基本都在碰撞矩形大小和图片实际显示区域不匹配上。要么是碰撞矩形用的是整个 pixmap 的完整矩形而 pixmap 四周有透明的 padding要么是矩形位置计算错误比如用了全局坐标和局部坐标混在一起运算。解决用 Qt 的调试手段对比两套坐标。把paintEvent里的绘制临时改成painter.drawRect(birdRect)和painter.drawRect(pipeRect)让碰撞矩形可视化地叠加在精灵图上。如果矩形明显突出图片可见区域就手动缩进几个像素如果位置整体偏移检查坐标是否都经过mapToParent或sceneBoundingRect处理。调完后把调试矩形注释掉再跑一遍看手感和判定是否一致。这个可视化调试的方法比猜数值强十倍。5.4 编译时报错“unknown module in qt: serialport”现象Project ERROR: Unknown module(s) in QT: serialport。原因.pro文件里写了QT serialport但当前 Qt 环境没有安装对应模块。这个项目本身没用到串口很可能是.pro文件被改动过或者复制自其他项目。解决打开flappyBird.pro把serialport从QT变量里删掉。本项目的依赖只有widgets最多加上multimedia如果用了音效。检查.pro文件里的QT 行只保留必要的模块然后重新运行 qmake。5.5 游戏在虚拟机上运行速度异常快现象在 VMware 或 VirtualBox 等虚拟机里跑游戏速度快到没法玩小鸟一秒钟飞过整个屏幕。原因这是定时器间隔和真实帧率脱钩的典型表现。如果gameLoop里直接用timer-start(16)固定 16 毫秒间隔那游戏逻辑是恒定的但 Qt 的QTimer在系统负载高时会出现积压导致在某一帧把之前累积的多次 timeout 一次性处理完。更极端的情况下如果项目代码里用了while循环配合QCoreApplication::processEvents()在虚拟机中会加速到不可控。解决改用真实时间差驱动。在gameLoop开头记录QElapsedTimer的经过毫秒数然后让所有移动量乘以时间归一化系数。常见做法deltaTime elapsed / 16.67移动速度乘以deltaTime即可让游戏在不同帧率下速度一致。这个修改看似细节但很多课设演示就是在这一步翻的车——在老师电脑上跑得好好的换台电脑就疯了。6. 参数调整与功能扩展改出你自己版本的 flappyBird6.1 主循环定时器与物理参数范围对照当你想做二次开发时优先调这几个参数每个参数都有明确的边界参数默认参考值可调范围调大效果调小效果timer-start()16 ms8~33 ms游戏加速操作响应快游戏减速操作迟钝gravity0.5 px/帧²0.3~0.8下落更快难度高下落更慢容错高jumpSpeed-8 px/帧-5~-12跳跃更高操控飘跳跃低操控沉水管缺口高度150~180 px120~220容易通过难度低难通过难度高水管水平速度2 px/帧1~4水管移动快水管移动慢调参的原则是**先调水管水平速度再调重力最后调跳跃力度。**因为跳跃力度和水管速度互相影响如果水管快了却保持原来的跳跃力度玩家会感到“按不过去”反过来也成立。我做过实测水管速度 3px/帧、重力 0.6、跳跃 -10 的组合下游戏通关率大致在 40% 左右属于比较好上手的课设演示难度。6.2 扩展一增加最高分持久化QSettings 用法默认的 flappyBird 只有单局分数关掉程序分数就没了。用 Qt 自带 QSettings 加一个持久化最高分记录改动量极小// 保存最高分 void widget::saveHighScore() { QSettings settings(MyCompany, FlappyBird); int lastHigh settings.value(highScore, 0).toInt(); if (score lastHigh) { settings.setValue(highScore, score); } } // 读取最高分 int widget::loadHighScore() { QSettings settings(MyCompany, FlappyBird); return settings.value(highScore, 0).toInt(); }这段代码里QSettings(MyCompany, FlappyBird)的前两个参数是组织名和应用名它们决定了配置文件在操作系统里的存储路径。Windows 上会写到注册表Linux 上写到~/.config/MyCompany/FlappyBird.conf。课设答辩时加上这个功能评分老师会觉得你考虑了“用户体验”而不只是写出一个能跑的游戏。6.3 扩展二把小鸟换成一个跳跃的方块精灵素材替换是最容易但坑最多的扩展方式。替换小鸟的图片时注意新素材的大小必须和原来的鸟图保持一致或接近——比如原图是 34x24你换成一张 100x100 的图碰撞矩形还是原来那个 34x24 的区域视觉上小鸟明明还没碰到水管却被判定碰撞体验会非常奇怪。正确的替换步骤是新图放进image目录在 Qt Creator 里右键src.qrc选择“添加现有文件”选中新图然后在代码里把加载 pixmap 的地方换成新文件名。如果新图和原图尺寸差得远同时调整birdRect的宽高并重新跑一遍碰撞矩形可视化的调试流程。我自己每次换素材必做的一件事就是在 paintEvent 里临时画出 birdRect确认碰撞范围和图片视觉边缘是否对齐。不这样做的话十有八九会出现判定漂移。6.4 发布时的 Release 构建与依赖排查课程设计要交的是能独立运行的 exe 文件这里指向 Qt 的依赖部署。Qt 程序不像普通 C 程序那样编译完就能随便拷走它依赖Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll等一堆动态链接库。发布前用 Qt 自带的windeployqt.exe打包# 用 Release 模式重新编译 # 假设你已经构建完成并拿到 flappyBird.exe set PATHC:\Qt\5.15.2\mingw73_64\bin;%PATH% cd 你的构建目录 windeployqt flappyBird.exewindeployqt会自动分析flappyBird.exe依赖了哪些 Qt DLL并把它们复制到同目录同时把 plugins 目录里面是平台插件比如qwindows.dll也拷贝好。打包完成后整个文件夹就能拷到任何 Windows 机器上直接运行。有一个易踩坑的地方如果构建用的是 Debug 模式windeployqt会提示你用--debug参数而且 Debug 版依赖的 DLL 体积更大。交课设前务必将构建套件切到 Release 模式重新构建一次。还有一个经典问题生成的 exe 运行时提示缺少libgcc_s_seh-1.dll或libwinpthread-1.dll这是 MinGW 的运行时库把 Qt 安装目录下bin文件夹里的这两个文件一并拷过去即可。6.5 一个值得保留的工程习惯碰撞矩形可视化开关最后分享一个我写 Qt 游戏时固定保留的小技巧——在 widget 类里加一个调试开关// 调试开关置为 true 时绘制碰撞矩形 bool debugDrawRects false; // paintEvent 末尾添加 if (debugDrawRects) { painter.setPen(QPen(Qt::red, 1)); painter.setBrush(Qt::NoBrush); painter.drawRect(birdRect); painter.drawRect(roadRect); for (pipeItem *pipe : pipes) { painter.drawRect(pipe-getTopPipeRect()); painter.drawRect(pipe-getBottomPipeRect()); } }平时这个开关保持 false不影响游戏画面调试时用QShortcut绑定一个按键比如 F2来动态切换。从那以后我每次改动碰撞参数都强制自己过一遍这个调试视图再关掉。这个习惯帮我避开了无数次“看着能过、一碰就死”的尴尬。希望帮到你。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
5分钟搭QQ智能体:Lighthouse接入与DeepSeek驱动实践 每天在网页版AI里复制粘贴、来回切换标签页,说实话挺累的。手机端就算装了APP,也得专门点开、等加载、再手动输入。后来我想明白了一件事:既然日常聊天都在QQ上,那为什么不能把AI直接塞进QQ里?这样随时随地发条消息就能… · 2026/9/24 21:58:04
5分钟搭建QQ智能体:Lighthouse对接DeepSeek完整实战指南 1. 项目整体设计与思路拆解1.1 为什么要把AI搬进QQ大部分人用AI的方式还停留在打开网页版对话窗口,复制粘贴问题,再手动把答案搬回来。这个流程最大的问题不是慢,而是“割裂”——你在写代码、写文档、处理日常消息时,频繁切换窗口… · 2026/9/24 21:58:04
大模型推理优化与部署实战:量化蒸馏模型选型全解析 1. 推理优化与部署的整体思路拆解1.1 为什么推理优化是模型落地的第一道坎训练一个大模型,动辄几十上百张卡跑几周,但真正决定这个模型能不能用起来、用得起、用得爽的,其实是推理阶段。我见过太多团队,模型训得漂漂亮亮ÿ… · 2026/9/24 21:58:04
IPv6地址从入门到实战:规则、类型与家庭网络配置 1. 为什么今天要花时间搞明白IPv6地址2. IPv6地址的书写规则与结构拆解3. 地址类型与生命周期:单播、组播、任播与临时地址4. 报头简化与关键机制取舍:IPv6“快”在哪里5. 家庭网络环境下,IPv6能带来哪些实际价值与配置要点其实你早就在用IPv… · 2026/9/24 22:35:44
YOLOv5+Python三合一:人脸识别表情识别异常行为检测系统解析 简介:基于Yolov5Python实现的人脸识别、细粒度表情识别与异常行为检测源码,面向准备毕业设计、期末大作业或课程设计的高校学生,也适合希望快速上手YOLOv5视觉应用、开展人脸与行为分析的开发者。压缩包内共107个文件,以37个py源码… · 2026/9/24 22:35:44
Microsoft Copilot 进阶指南:从聊天到 AI 代理,打造自动化办公流程 你是不是也把 Microsoft Copilot 当成了一个“高级搜索引擎”?问一句答一句,拿到答案就关掉,然后继续在 Word、Excel、邮件、会议之间来回切换,手动做那些重复又琐碎的整理工作。如果是这样,那你的 Copilot 可能只发挥… · 2026/9/24 22:35:44
Java final关键字深度解析:变量、方法与JVM内存语义实践 我在团队里带过不少刚转Java的同事,每次看他们代码,发现一个很有意思的现象:final这个关键字几乎人人都知道,但真正能用对的没几个。要么到处final导致代码又长又啰嗦,要么该加final的地方完全没加,等到排查… · 2026/9/24 22:35:44
Agent Skills实战:从设计原则到参数约束与调试方法 做AI Agent这么长时间,我越来越觉得“技能”才是决定一个Agent上限的东西。模型本身大家用的都差不多,真正拉开差距的,是你给Agent装了什么可复用的执行能力,以及这些能力被设计得有多好用。今天就把我在实际项目和agent-skills这… · 2026/9/24 22:35:44
从继承到装饰器:Java通知模块重构实战,告别组合爆炸 大概三年前的某个深夜,我盯着项目里那十七个以Notify开头的类,第一次认真琢磨装饰器模式(Decorator Pattern)到底能救多少代码。当时那是一个消息通知模块,需求方从“先发个短信就行”一路加码到“短信邮件站内信都要、… · 2026/9/24 22:35:31
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44