ARTICLE DETAIL

资讯详情

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

C++ Qt跑酷游戏源码解析:课程设计高分实战

C++ Qt跑酷游戏源码解析:课程设计高分实战 简介一款面向计算机相关专业学生与Qt初学者的C课程设计资源基于Qt框架实现跑酷小游戏源码完整、可编译运行适合正在准备期末大作业或需要项目实战练习的开发者。包内共72个文件涵盖7个cpp源码、6个h头文件、2个ui界面文件以及pro项目管理、qrc资源索引和53张png图片素材压缩包约3.46MB代码按界面、场景、角色等模块划分结构清晰便于理解游戏循环、碰撞检测与Qt界面设计。该项目为大二高分大作业评审得分99分功能完整涉及角色控制、障碍生成、飞镖攻击、计分逻辑等多个模块并附有README说明能帮助读者快速掌握游戏框架与二次开发思路。同时资源附带清晰的目录结构说明方便定位关键代码游戏素材齐全可直接替换图片定制主题。目前已有94人学习下载作为课程设计参考或Qt游戏开发入门范例均有较高价值。1. 把“高分课程大作业”当成一个能跑的产品来拆这套 Qt 跑酷源码到底在给你什么距课程设计提交只剩一周你打开 Qt Creator新建了一个 QWidget 工程然后对着空窗口发呆——这是很多人在做 C 课程大作业时的真实状态。C 基于 Qt 框架的跑酷小游戏源码乍看是个“小游戏”实际是一份标准的 Qt Widgets 项目骨架游戏循环用 QTimer 驱动、画面用 QPainter 绘制、交互靠键盘事件接管再加上重力模拟、碰撞检测和积分系统正好覆盖了 Qt 课程里最常考的几个知识点代码量也足够写进“项目概况”那一页 PPT。它适合两类人一是要以“能跑、能演示、能答辩”为底线的课程设计选手二是刚学完 C 想找个图形界面项目练手的 Qt 新手。这篇笔记按我的落地习惯把这套源码拆成框架、玩法、高分结构和编译调试点四层照着复现完你手里就是一份能理直气壮交上去的作业。2. 用 QTimer 和 QPainter 搭出跑酷最小循环三个类撑起一个能交差的 Demo2.1 为什么选 QWidget QPainter 而不是 QML先回答一个常见疑问Qt 做游戏不是有 QML 吗QML 做动画、做粒子效果确实比 Widgets 省事很多但课程作业这条路线有坑。QML 虽然写界面快大量逻辑却落在 JavaScript 这一层答辩时老师问“你的类封装在哪、继承关系是什么”你很难讲清楚。Widgets 这边界面是一个 QWidget 子类绘制、事件、状态全部收在 C 类里代码量也实打实摆在那里目录结构一眼就是“正经项目”。如果你只在 Qt Creator 里见过 QML 模板这次建议直接选 Widgets Application省去不必要的学习成本。另一个理由是编译链路简单。QML 工程在 Qt 5.15 上往往要带 qml 目录、qrc 资源、还有一堆 import换到室友的机器上部署时少一个文件就白屏。Widgets 程序编译出来就是一个 exe配合 windeployqt 打包拷贝整个文件夹就能跑。对课程提交来说可迁移性就是安全感。2.2 游戏主循环用 QTimer 驱动 onTick而不是 while(1)跑酷游戏本质是一个不断刷新的状态机每个刷新周期做三件事处理输入、更新实体位置、重绘画面。Qt 里最稳的做法是用 QTimer 定时触发一个槽函数而不是自己在子线程里跑 while(1)。原因很简单QPainter 绘制必须发生在 GUI 线程的事件循环里你在子线程里画界面要么崩溃、要么黑屏而 QTimer 的 timeout 信号天然就在主线程。下面是最小工程的类声明三个类各管一摊GameWidget 管窗口和调度Player 管角色状态Obstacle 管障碍物实体。// gamewidget.h — 游戏窗口骨架 class GameWidget : public QWidget { Q_OBJECT public: explicit GameWidget(QWidget *parent nullptr); protected: void paintEvent(QPaintEvent *event) override; void keyPressEvent(QKeyEvent *event) override; private slots: void onTick(); // 每个定时器周期调用一次 private: QTimer m_timer; // 游戏主循环驱动 Player m_player; // 玩家实体 QVectorObstacle m_obstacles; // 障碍物列表 int m_score 0; // 当前分数 bool m_running false; // 游戏是否进行中 int m_elapsedFrames 0; // 累计帧数 };构造函数里要做的不是启动一堆逻辑而是把定时器参数定好把键盘事件接住。// gamewidget.cpp — 构造函数 GameWidget::GameWidget(QWidget *parent) : QWidget(parent) { setFocusPolicy(Qt::StrongFocus); // 必须设置否则收不到键盘事件 setFixedSize(800, 400); // 锁死窗口坐标逻辑不用考虑缩放 connect(m_timer, QTimer::timeout, this, GameWidget::onTick); m_timer.setInterval(16); // 约 60 FPS 刷新 }这里有两个参数值得记住。setInterval(16)是 60 FPS 的标准帧间隔显示器的垂直同步节奏也是 60Hz这样角色移动看起来不跳帧。setFixedSize(800, 400)锁死窗口后paintEvent 里的所有坐标都可以按绝对值写死不用处理窗口拉伸带来的换算这个决定能在后面帮你省掉一大块布局调试时间。如果你想把游戏窗口嵌进更大的页面可以把固定尺寸改成 resize但障碍物生成坐标和地面高度都要跟着改属于吃力不讨好课程设计不推荐。2.3 onTick 里更新逻辑paintEvent 里只画状态游戏循环的正确姿势是把状态更新集中在一个地方把绘制集中在另一个地方两者通过成员变量通信。很多新手在 paintEvent 里写角色移动一刷新就 new 一堆临时对象结果整个程序卡得像幻灯片。这里给出 onTick 的完整实现void GameWidget::onTick() { if (!m_running) { return; // 游戏结束或未开始时不更新逻辑 } m_elapsedFrames; // 帧计数前进一格 m_player.update(); // 先更新角色物理重力、跳跃、落地 // 障碍物生成每 90 帧生成一个速度随分数提升 int spawnInterval qMax(30, 90 - m_score / 200); if (m_elapsedFrames % spawnInterval 0) { m_obstacles.append(Obstacle(width())); } // 障碍物移动与碰撞检测 for (int i m_obstacles.size() - 1; i 0; --i) { m_obstacles[i].moveLeft(2 m_score / 500); if (m_player.collidesWith(m_obstacles[i])) { m_running false; // 只改状态结算交给独立函数 } if (m_obstacles[i].x() m_obstacles[i].width() 0) { m_obstacles.removeAt(i); // 出屏就删防止容器膨胀 } } update(); // 请求 Qt 重绘当前窗口 }这段代码里有两个细节必须说透。第一for 循环从size() - 1往前遍历再removeAt(i)是因为删除元素会让后面的索引前移正序遍历会跳过紧挨着被删元素的下一个从后往前删永远不会漏。第二update()只是一个请求标记Qt 会在事件循环空闲时调用 paintEvent你不在 onTick 里直接绘制就不会出现画面撕裂和重入问题。之后 paintEvent 只需要读取成员变量画出来。地面的绘制、玩家的矩形、障碍物的矩形全部用 QPainter 的基本图元完成20 行以内就能写完。这个“更新与绘制分离”的结构也是后两章一切高分支撑的基础。3. 跳跃、障碍与碰撞检测把“跑酷感”落到算法而不是手感调参3.1 重力与跳跃一帧一积分让角色有“物理感”跑酷游戏的“跑酷感”不是靠随机抖动调出来的而是靠一组稳定的物理参数。常见做法是把角色的 Y 坐标改为由速度积分得到每帧给垂直速度叠加一个固定的重力加速度然后按速度移动位置。伪物理模型看起来简单但参数配不好就出现两种情况重力太小角色飘在天上重力太大落地像钢板砸下来。下面是我常用的参数组合按 60 FPS 帧率设计。void Player::update() { // 跳跃只在“未起跳”时消费按键请求 if (m_jumpRequested !m_isJumping) { m_vy -8.0f; // 初速度负方向为向上 m_isJumping true; m_jumpRequested false; // 请求消费掉避免连跳 } if (m_isJumping) { m_vy 0.4f; // 重力加速度 m_y m_vy; // 按速度积分位置 if (m_y m_groundY) { m_y m_groundY; // 落地钳制不穿地面 m_isJumping false; m_vy 0.0f; } } }参数说明初速度 -8.0、重力 0.4、地面高度按窗口 400 像素下取 300。这个组合的跳跃最高点在起跳后大约 20 帧出现峰值高度约 80 像素刚好能越过一个 40 像素高的障碍物又不至于一蹦就撞到窗口顶部。如果你想让手感更轻快把初速度改成 -9.0、重力改成 0.35跳跃弧线会更高更飘想走沉重路线就反过来。唯一的原则是调整后必须把“能否跳过最大障碍物”重新验证一遍这是最常见的玄学调参翻车点。跳跃按键不能直接在 keyPressEvent 里改坐标而是只记录请求。void GameWidget::keyPressEvent(QKeyEvent *event) { if (event-key() Qt::Key_Space) { if (!m_running) { startGame(); // 死亡后按空格重开 } else { m_player.requestJump(); // 写请求不写坐标 } } QWidget::keyPressEvent(event); }原因很简单keyPressEvent 是事件驱动可能一秒钟触发好多次而 update 是固定频率。如果你在按键事件里直接改 m_y角色就会把多次按键的位移叠加在同一帧里直接飞出屏幕。把请求和消费分开是 Qt 游戏循环里最值得养成的一个习惯。3.2 障碍物生成让出屏节奏可用公式表达障碍物生成不能完全随机否则会出现连续三根柱子堵死路线的“死局”。常见做法是用帧计数取模来定间隔再在间隔内加少量随机偏移。下面这个 Obstacle 构造函数在窗口右缘生成实体Obstacle::Obstacle(int windowWidth) { m_x windowWidth; // 从右缘进入视野 m_w 40 qrand() % 30; // 宽度 40~69 像素 m_h 40 qrand() % 40; // 高度 40~79 像素 m_y kGroundY - m_h; // 底部贴地高度从地面向上生长 }需要说明的是qrand()在 Qt 5 里仍然可用但你必须先qsrand(QTime::currentTime().msec())做种子初始化否则每次运行生成的障碍物序列完全一样答辩演示时会被老师一眼看穿。更稳妥的做法是用 C11 的std::mt19937配合随机设备代码只多两行但“这里用了现代 C 随机数”可以成为答辩加分句。障碍物不能只随机宽度和高度位置节奏也要留出反应时间当障碍物在 800 像素宽的窗口里以每帧 2 像素的速度左移时从出现到到达角色大约需要 200 帧约 3.3 秒这个窗口期足以让玩家做出一次完整判断。3.3 AABB 碰撞检测矩形相交判断的四个边界跑酷游戏的碰撞检测不需要像素级精确AABB轴对齐包围盒就够了。所谓 AABB就是把角色和障碍物都看成矩形然后判断两个矩形是否相交。相交的数学条件是两个矩形在 X 轴投影重叠同时在 Y 轴投影也重叠。bool Player::collidesWith(const Obstacle ob) const { int px1 m_x, py1 m_y; int px2 m_x m_w, py2 m_y m_h; int ox1 ob.x(), oy1 ob.y(); int ox2 ob.x() ob.width(), oy2 ob.y() ob.height(); return px1 ox2 px2 ox1 py1 oy2 py2 oy1; }这里必须用严格小于和严格大于不能用和。原因很微妙两个矩形刚好贴边时视觉上还没有重叠但用小于等于就会判定为碰撞角色会“隔空而死”。另一个坑是碰撞检测的时机如果障碍物每帧移动 2 像素而角色每帧最多移动 1 像素那么连续两帧之间不会跳过重叠区域不需要做更细的插值检测但如果你调了速度参数让单帧移动超过角色宽度的一半就会出现“穿模过墙”这时才需要把检测拆成两段。对课程作业的速度量级这个 AABB 足够。分数结算也放在帧计数轴上每帧加一个固定分同时把高分写入成员变量。注意不要每帧都调用update()去刷新 QLabel 上的分数文字那样会拖慢主循环分数文字可以用独立 QTimer 每 500 毫秒刷新一次或者直接在 paintEvent 里用drawText画在画布上。课程设计建议用后者少一个控件就少一组信号槽要管理。4. 课程大作业的高分源码结构让答辩老师一眼看到你的封装能力4.1 把绘制和逻辑分开paintEvent 里不许出现业务代码我见过不少拿低分的 Qt 大作业问题不在功能不全而在代码全堆在 paintEvent 和 keyPressEvent 里。老师打开源码看到 300 行的 paintEvent第一印象就是“结构不清晰”后面功能做得再好也难翻身。高分的源码结构核心就一句话paintEvent 里只读取成员变量并绘制不做任何逻辑判断、不 new 对象、不调用业务函数。我一般会在绘制代码里遵循三条纪律。第一地面、背景、分数这些静态元素放在一个drawBackground()私有函数里角色和障碍物分别进drawPlayer()和drawObstacles()。这样新增一个绘制元素就是新增一个函数而不是往 paintEvent 里再塞 20 行。第二所有坐标计算提前在 onTick 里完成paintEvent 只做 QPainter 的搬运工。第三涉及图片资源时在构造函数里把 QPixmap 一次性加载并缓存为成员变量绝不放在 paintEvent 里加载——文件 I/O 阻塞会让画面间歇卡顿这是“程序一跑就掉帧”的常见病因。一个可参考的绘制分离结构void GameWidget::paintEvent(QPaintEvent *event) { QPainter painter(this); drawBackground(painter); // 地面、天空渐变 drawScore(painter); // 分数文字 drawPlayer(painter); // 玩家矩形或精灵图 drawObstacles(painter); // 障碍物列表 drawStateText(painter); // “按空格开始”等提示 QWidget::paintEvent(event); }这段代码的价值不是省几行而是让读代码的人一眼看出画面由哪几部分组成。答辩时你甚至可以对着这个函数讲“整个游戏的画面分五个图层绘制背景在最底状态文字在最上。” 这句话本身就在回应评分表里“程序结构清晰”那一栏。4.2 四状态机用 enum 管住开始、运行、暂停、死亡游戏流程只用 bool 变量管理是新手最容易翻车的地方。m_running 一个 bool 只能表达“开”和“关”但跑酷游戏实际有四个状态待开始、运行中、暂停、死亡。用 bool 组合去区分这四种状态代码会变成一堆if (m_running !m_paused)改一个条件就牵动全局。我习惯的做法是引入枚举状态机enum class GameState { Ready, // 待开始显示标题和操作提示 Running, // 运行中正常刷新逻辑 Paused, // 暂停QTimer 继续走但逻辑不更新 Dead // 死亡显示得分和重开提示 };配合状态机onTick 里的分支变得非常清晰void GameWidget::onTick() { switch (m_state) { case GameState::Running: m_elapsedFrames; m_player.update(); // ...障碍物生成、移动、碰撞检测 update(); break; case GameState::Paused: // 不更新游戏逻辑但可以在这里做菜单动画 break; default: break; // Ready 和 Dead 都不需要逻辑更新 } }状态机的优越性在键盘事件里体现得更明显。按键变成事件时先查状态Running 时按空格是跳跃Dead 时按空格是重开Ready 时按空格是开始。这三个动作如果要靠 if 拼 bool 变量写着写着就会混。状态机是课程设计里性价比最高的“结构优化”代码量只增加 10 行但答辩时你能说出“我用了状态机管理游戏流程”直接命中评分点。4.3 参数配置集中化把魔数清零让调参只改一处一份拿高分的源码不应该出现遍地都是的 8.0f、0.4f、90 帧这些魔数。把这些参数集中放是一个很显功力的动作。常见做法是在项目里加一个Config命名空间namespace Config { constexpr int kGameWidth 800; constexpr int kGameHeight 400; constexpr int kGroundY 300; constexpr float kGravity 0.4f; constexpr float kJumpVelocity -8.0f; constexpr int kBaseSpawnInterval 90; constexpr int kBaseSpeed 2; }所有类引用配置时都写Config::kGravity而不是 0.4f。这带来一个实际好处你调游戏手感时只需要开这一个文件改完重新编译就能看效果不用在十个文件里 CtrlF 找魔法数字。更进一步把玩家体积、障碍物尺寸范围也放进 Config复现这套源码的人改几行就能变成不同难度版本这也是“可扩展性”的直接体现。资源文件也要用 Qt 资源系统管理而不是依赖本地图片路径。新建一个 res.qrc把图标、背景图、音效文件全部加进去代码里用:/images/player.png这种前缀路径访问。用 qrc 的好处是发布时只有一个 exe 加一个可选的 dll 目录不会出现图片找不到的黑屏事故。课程设计提交时老师解压你的压缩包找到 exe 就能跑比“需要把图片放在指定目录”的版本可靠得多。4.4 类与文件划分让目录结构自己会说话高分源码的最后一个特征是文件划分合理。常见的划分方式是五个文件main.cpp 只管创建 QApplication 和主窗口gamewidget.cpp/h 是游戏窗口负责调度player.cpp/h 是角色逻辑obstacle.cpp/h 是障碍物实体config.h 是参数集中地。如果障碍物有多种类型再拆出 obstacle 基类和两个子类。文件拆分的原则是“换一个功能只动一个文件”。要改跳跃力度只改 player.cpp要改障碍物生成节奏只改 gamewidget.cpp 里的生成逻辑要加新障碍物继承 Obstacle 后改生成分支。这种内聚度在答辩时很容易被注意到老师翻一下文件列表就能看出你对“高内聚低耦合”的理解不是背概念而是在代码里做出来了。顺便说一句类和文件名一定要和内容对得上见过太多 student.cpp 里装着一坨游戏逻辑的作业那种代码不管功能多全在老师那里第一印象已经降价了。5. 编译与运行避坑Qt 5.15 的 dependent 报错、定时器崩溃和中文乱码5.1 编译期报错error: dependent ..\qt\5.15.2\msvc2019_64\include\qtwidgets打不开这是 Qt 5 课程作业里最经典的开局翻车。现象是编译报错错误信息长这样:-1: error: dependent ..\..\..\..\..\..\qt\5.15.2\msvc2019_64\include\qtwidgets does not exist后面还可能跟着一堆QWidget: No such file or directory。看到这么长的相对路径第一反应别去检查 Qt 安装目录问题基本出在 .pro 工程文件上。原因Qt 5 分成 core、gui、widgets 等多个模块只有 core 和 gui 是默认链接的。你在代码里#include QWidget用的是 qtwidgets 模块的头文件但 .pro 里没声明要链接这个模块qmake 就不会把头文件搜索路径加进来于是编译器顺着一个不存在的相对路径去找头文件报出这串吓人的英文。解决方法是打开 .pro 文件确认里面至少有这行QT core gui widgets greaterThan(QT_MAJOR_VERSION, 4): QT widgets第一行是显式声明要 Qt Widgets 模块第二行是兼容 Qt 4 的老写法很多模板会带上。新建工程如果选的是 “Qt Widgets Application” 模板这行会自动写进去如果是手敲的 .pro 或者从旧工程改的十有八九就漏在这里。改完 .pro 后重新 qmake再编译这个报错就会消失。这条的经验是Qt 的报错信息里那个一长串..\开头的路径不是在告诉你文件在哪里而是在告诉你有某个模块没有启用。5.2 运行时崩溃关掉窗口程序就崩定时器还在后台跳现象游戏运行正常但点窗口右上角的关闭按钮时程序偶尔闪退或者退出后任务管理器里还留着进程。更隐蔽的变体是关闭游戏窗口后另一个界面还在不断刷新日志持续输出。原因QTimer 的 timeout 信号连接到了某个槽函数而槽函数所属的对象在窗口销毁时已经被释放但定时器没有停止。Qt 的信号槽机制在连接存在且发送者仍存活时会尝试调用接收者的槽。如果接收者已经 delete对应的就是“访问已释放内存”轻则静默崩溃重则随机 SEGFAULT。另一个常见病因是在构造函数里用 lambda 捕获了 this而这个 lambda 对象生命周期长于窗口。解决方法是养成两个习惯。第一在 GameWidget 的析构函数里显式m_timer.stop()这是常规解法任何时候都不会错。第二所有 connect 都指定接收者上下文为 thisconnect(m_timer, QTimer::timeout, this, GameWidget::onTick); // 接收者 this安全如果你用了 lambda 连接也把 this 作为第三个参数传进去让 Qt 在 this 销毁时自动断开连接。这个习惯在 Qt 5 里能避开九成由生命周期引起的崩溃。另外补充一条排查手段在 Qt Creator 的“应用程序输出”面板里看到The program has unexpectedly finished时用调试模式运行崩溃时调用栈会停在出问题的代码行比瞎改参数有效得多。血泪经验是关闭窗口崩溃这类问题十次里有八次是定时器或 lambda 生命周期不要先去怀疑你的碰撞检测写错了。5.3 中文乱码按钮上的“开始游戏”变成了一串符号现象在 Qt Creator 源码里写的中文字符串编译运行后显示成乱码但英文完全正常。有的人换一台电脑又好了显得很“玄学”。原因这是编码链不一致的问题。源码文件本身是 UTF-8 编码Qt Creator 默认但 MSVC 编译器在没有显式指示时默认按本地代码页读源文件中文 Windows 下是 GBK。于是编译器把 UTF-8 的字节流当成 GBK 去解析字符串字面量里的中文字节就被翻译成了错误的字符序列运行时就显示乱码。Qt 5 的QString存储的是 Unicode但源代码这一步已经歪了后面怎么转都补不回来。解决方法是让编译器强制按 UTF-8 读源码在 .pro 里加一行msvc: QMAKE_CXXFLAGS /utf-8这个参数让 MSVC 把源文件和目标文件都按 UTF-8 处理中文字符串字面量表现和 GCC 一致。如果你用的是 MinGW 编的 Qt默认就是 UTF-8一般不会遇到这个问题。还有一个偏方是把源码文件转成 UTF-8 with BOMMSVC 看到 BOM 也会切换到 UTF-8 解析但每加一个文件都要手动转非常烦。统一用/utf-8编译选项是最省心的。另外不要在源码里写QString(中文)这种老写法改用QStringLiteral(中文)或QString::fromUtf8(中文)性能更好语义也明确。如果你做完这步还乱码检查一下是不是用了tr()包中文字符串Qt 5 的翻译机制在国内课程作业场景下反而容易引入二次编码问题直接去掉最干净。5.4 高分屏下窗口模糊、点击偏移高 DPI 缩放两个坑现象在 2K 或 4K 显示器上编译出来的程序窗口很小、文字模糊鼠标点击的位置和界面元素对不上。这通常出现在 Windows 高分屏上DPI 缩放一介入Qt 默认策略就出问题。原因Windows 会把高分辨率显示器的缩放比例设为 125% 或 150%而 Qt 5.15 默认不感知缩放界面按逻辑像素绘制再被系统拉伸自然模糊。点击偏移则是因为鼠标事件坐标和绘制坐标用了不同的缩放基准。解决是在 main.cpp 里QApplication 构造之前设置高 DPI 属性int main(int argc, char *argv[]) { #if QT_VERSION QT_VERSION_CHECK(5, 6, 0) QApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif QApplication::setHighDpiScaleFactorRoundingPolicy( Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); QApplication app(argc, argv); GameWidget w; w.show(); return app.exec(); }这几行的要点是setAttribute必须发生在 QApplication 对象创建之前写成全局 static 变量声明处也可以就是不能放在app构造之后否则设置无效。PassThrough策略让 Qt 使用非整数的实际缩放比例而不是四舍五入到 100% 或 200%能减少文字边缘发虚的现象。做完之后窗口初始尺寸会随缩放变大你setFixedSize(800, 400)设的是逻辑像素实际物理像素会按比例放大这是预期行为不用改。注意这行代码不同 Qt 版本行为有细微差别但 5.6 到 5.15 都兼容可以直接抄。5.5 换台电脑就闪退缺 Qt dll 和 VC 运行库现象在你自己机器上跑得好好的 exe拷到室友电脑上双击要么弹窗提示找不到 Qt5Widgets.dll要么直接报错 0xc000007b要么提示缺少 VCRUNTIME140.dll。原因Qt 默认是动态链接运行库你的 exe 在运行时需要找 Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll 这些动态库。这些库在你机器上有是因为 Qt 安装目录在 PATH 里室友机器没装 Qt自然找不到。0xc000007b 则多半是架构不匹配——你编的是 64 位拷到 32 位系统上运行位数对不上。解决方法是发布时带上 Qt 官方提供的部署工具。用 Qt 自带的命令行工具进入你编译输出目录执行windeployqt 你的游戏名.exe这个工具会把 exe 依赖的所有 Qt dll、插件目录、以及必要的运行库一并复制到当前目录然后把整个文件夹拷给室友即可。要注意windeployqt 也有 32 位和 64 位之分Qt Creator 里你配的构建套件是 MSVC2019 64-bit命令行里也要打开对应的 “Qt 5.15.2 (MSVC 2019 64-bit)” 快捷方式不能拿 MinGW 的部署工具去补 MSVC 编的 exe。另外MSVC 构建的程序默认还需要 VC 运行库如果你的目标机器是较干净的 Windows把对应版本的 vcredist 安装包一起放进去或者提前让室友装上。更省事的一条路是在 .pro 里加QMAKE_LFLAGS /STATIC开启静态链接把 Qt 核心库编进 exe但 Qt 5.15 的静态构建包需要自己去 Qt 官网下载课程作业通常不值得折腾windeployqt 已经够用。6. 最后一步提价QSS 美化、状态机补齐与 30 分钟验证6.1 用 QSS 把“学生界面”变成“完工界面”课程设计的界面往往是扣分重灾区因为默认 QWidget 长什么样大家的作业就长什么样。QSS 是这里最划算的投入在项目里加一个 qss 文件或者在 main.cpp 里用app.setStyleSheet(...)设置全局样式给背景、分数标签、按钮加上圆角和渐变视觉感受会提高一个档次。比如给分数区加一个透明黑底白字的效果只需要十几行QLabel#scoreLabel { color: #ffffff; background-color: rgba(0, 0, 0, 150); border-radius: 8px; padding: 4px 12px; font: bold 16px Microsoft YaHei; }注意 QLabel 要设置 objectName 为 scoreLabelQSS 才匹配得上。这个细节如果写进答辩介绍“我用 QSS 实现了界面美化”会比你口头说“界面挺好看的”有说服力得多。6.2 答辩前最值得做的三个验证功能写完到提交之间留出半天做验证按这个清单过一遍最省心。第一连续运行 30 分钟打开任务管理器观察内存占用曲线如果持续上涨说明有对象在循环里不断 new 而没有 delete回头查 onTick 里的临时对象和障碍物容器。第二快速连按空格、按住空格不松手看角色会不会出现“跳跃消失”或者卡进地面正常表现是每帧请求被正确消费角色只跳一次固定高度。第三把窗口从默认尺寸拖大再拖小如果界面元素没有错位说明你的布局是稳定的。这个验证如果放到演示前一天做可能一夜都在改代码。我做 Qt 课程设计那会儿的习惯是功能全跑通以后再花一天时间慢下来读自己写的代码把每个数字为什么是这个值都写进注释把状态机的分支画在纸上对一遍。答辩时老师盯着我看问“跳跃请求为什么不在 keyPressEvent 里处理而是放在 update 里”当时我能把原因讲清楚那种准备充分的从容比多加一个特效有用得多。希望帮到你。本文还有配套的精品资源点击获取
返回列表