
简介面向期末大作业与课程设计场景的Qt5打地鼠游戏完整源码曾获98分高分评价基于C语言编写并配有详细注释涵盖随机出洞、锤击判定、实时计分与游戏结束等核心机制界面简洁美观操作响应流畅新手也能轻松读懂并继续扩展功能。资源包共22个文件包含5个cpp实现源码、4个h头文件、ui界面文件、pro工程配置与qrc资源索引另配8张png图片及1张jpg界面素材压缩包仅226KB工程体量轻量、目录组织清晰。已有50人学习下载对需要完成同类型小游戏开发或初次搭建Qt工程的同学是一份可直接对照参考的完整样例。通过研读源码可理解C语言在图形界面编程中的实际应用、事件循环机制与多文件模块划分思路具备良好的学习与复用价值。1. 把 Qt5 打地鼠源码拆开看期末课程设计最稳的选题之一如果你正在为 C 语言课程设计发愁又不想写学生管理系统这种烂大街的题目打地鼠游戏是个相当聪明的选择。这个项目用 Qt5 的图形场景框架实现了一个完整的打地鼠游戏得分 98说明它在功能完整度、界面交互和代码规范上都有足够的底子。Qt 本身是 C 写的但只要你有 C 语言基础——指针、结构体、回调这些概念你摸过——读这份源码没有任何压力。它不是什么黑匣子场景管理、图元碰撞、定时器调度都是基本功拆开看一遍你不仅能交作业还能在答辩时把每个类的作用说清楚。适合谁来用期末交大作业的学生、想快速入手 Qt5 图形编程的初学者、以及需要一份「能跑、能讲、能改」的参考工程的人。接下来我从项目结构、核心机制、编译部署到踩坑记录一条线把它说透。2. 代码骨架与场景框架先从 .pro 和类划分看懂工程结构2.1 文件清单里藏着 MVC 的影子解压这份资源后你会看到beatMouse.pro、main.cpp、mainwindow.cpp/h、myscene.cpp/h、myitem.cpp/h、handler.cpp/h、img.qrc以及一堆图片资源。初次接触 Qt 的人容易懵——这些文件各干什么其实 Qt 的工程组织很规整.pro是构建配置文件main.cpp是入口mainwindow负责窗口和 UI 布局myscene继承QGraphicsScene管理游戏场景myitem继承QGraphicsItem定义地鼠图元handler处理业务逻辑.qrc把图片打包进资源系统。这个分层思路跟 MVC 是呼应的场景是模型和视图的中间层图元是视图元素handler 是控制器。你在答辩时只要把这个对应关系讲清楚老师就知道你不是抄的。下面看.pro文件的核心内容QT core gui greaterThan(QT_MAJOR_VERSION, 4): QT widgets TARGET beatMouse TEMPLATE app SOURCES main.cpp \ mainwindow.cpp \ myscene.cpp \ myitem.cpp \ handler.cpp HEADERS mainwindow.h \ myscene.h \ myitem.h \ handler.h FORMS mainwindow.ui RESOURCES img.qrcQT core gui声明依赖 Qt 的核心模块和 GUI 模块greaterThan(QT_MAJOR_VERSION, 4): QT widgets是兼容 Qt4/Qt5 的写法Qt5 里图形控件类都挪到了 widgets 模块这一行必须保留否则编译直接报找不到QMainWindow。TEMPLATE app表示生成可执行程序而不是库。2.2 场景、图元与事件处理的三角关系myscene继承QGraphicsScene它的职责是管理所有图元的位置、碰撞检测和刷新。myitem继承QGraphicsItem它重写了paint()和boundingRect()地鼠的三种状态——缩在洞里、冒头、被打中——就是靠切换不同图片实现的。handler类负责定时器调度和计分逻辑它通过 Qt 的信号槽机制跟场景和窗口通信。// 在 myscene 构造函数中创建地鼠图元并放入场景 for (int i 0; i ROWS; i) { for (int j 0; j COLS; j) { MyItem *item new MyItem(); item-setPos(i * (HOLE_WIDTH GAP), j * (HOLE_HEIGHT GAP)); connect(item, MyItem::beaten, this, MyScene::onItemBeaten); addItem(item); m_items.append(item); } }setPos按照行列坐标把地鼠摆到洞口位置connect把每个图元的beaten信号连到场景的onItemBeaten槽函数上。这里注意ROWS和COLS是定义在头文件里的常量改这两个数就能调整游戏难度——洞越多玩家反应时间越短。boundingRect()必须返回正确的矩形范围否则点击判定会偏。常见做法是让它跟图片实际显示尺寸一致QRectF MyItem::boundingRect() const { return QRectF(0, 0, 100, 100); }图元的事件处理靠重写mousePressEventvoid MyItem::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (m_state State::Up) { m_state State::Beaten; emit beaten(m_score); update(); event-accept(); } else { event-ignore(); } }只有地鼠处于Up状态时点击才计分否则事件向下传递。这个设计很巧妙——它避免了空洞也被点击计分的逻辑漏洞答辩时可以重点提。3. 编译与部署Qt Creator 打开工程到跑起来的完整流程3.1 环境选择Qt5 版本与编译器的搭配这份资源基于 Qt5 开发建议直接用 Qt 5.12 到 5.15 之间的 LTS 版本原因很简单新版本 Qt6 的兼容层虽然存在但部分旧 API 已经调整没必要给自己找麻烦。Windows 下选择 MinGW 64-bit 编译器套件最省心因为 Qt 官方安装包自带配套的 MinGW不需要额外配置。如果你机器上已经有 VS 2019 或 2022也可以装 MSVC 版本但首次配置时要注意调试器版本匹配。下载 Qt5 安装包时组件选择勾选Qt 5.x.x MinGW 8.1.0 64-bit和Qt Creator两项就够了不用全选节省磁盘空间。安装路径不要带中文C:\Qt是社区最常见的选择。3.2 一步步把工程跑起来第一步打开 Qt Creator点击「文件」→「打开文件或项目」定位到解压目录里的beatMouse.pro双击后 Qt Creator 会弹出编译套件选择界面。建议只勾选 MinGW 64-bit 一套避免混用。第二步确认构建目录。默认是工程同级目录下的build-beatMouse-Desktop_Qt_5_x_x_MinGW_64_bit-Debug这个不用改但要注意整个工程路径里不能有空格和中文否则编译阶段会出现匪夷所思的路径解析错误。第三步点击左下角的绿色三角号直接运行。如果一切顺利你会看到游戏窗口弹出背景图加载正常地鼠在洞口随机冒出。如果出现黑屏或者图片丢失问题大概率出在资源加载部分我在第 4 章会详细说。编译时终端输出的关键信息14:32:08: Running steps for project beatMouse... 14:32:09: Starting: C:\Qt\Tools\mingw810_64\bin\mingw32-make.exe -f Makefile.Debug 14:32:15: The process C:\Qt\Tools\mingw810_64\bin\mingw32-make.exe exited with code 0. 14:32:15: Elapsed time: 00:07.看到exited with code 0就说明链接成功这一步是很多新手第一次接触构建系统的门槛——实际上你不需要理解 make 的细节只要知道 exit code 0 等于成功即可。如果报错代码非 0往下看错误列表Qt Creator 会定位到具体文件和行号双击就能跳转。3.3 资源文件 img.qrc 的结构与坑点Qt 的资源系统把图片统一编译进二进制文件里发布时不需要带图片文件夹这是它的优点。但.qrc文件的路径书写是踩坑高发地RCC qresource prefix/ fileimg/bg.png/file fileimg/bg1.png/file fileimg/chui.png/file fileimg/chui_up.png/file fileimg/chui_down.png/file fileimg/beat.png/file /qresource /RCC代码里引用资源时路径是:/img/bg.png而不是相对路径冒号前缀是 Qt 资源系统的固定写法。很多同学把图片文件放在源码目录的img文件夹下qrc里也写的img/bg.png但运行时图片加载不出来——问题出在工作目录跟源码目录不一致。Qt Creator 默认的工作目录是构建目录不是源码目录。解决办法有两个一是像这份资源一样用qrc资源系统让图片跟随可执行文件走二是每次运行前手动设置工作目录。明显第一种更省事这也是它的img.qrc存在的意义。4. 避坑图片加载、中文乱码与信号槽的四个翻车现场4.1 图片加载失败背景图消失或地鼠成白块现象运行程序后窗口弹出来了但背景是纯灰色的地鼠看不见或者偶尔闪现白块。原因第一排查qrc文件里的路径和实际路径是否一致prefix/条件下代码引用写:/背景图.png就是错的应该是:/img/bg1.png。第二排查图片格式.jpg图片在 Qt 里默认只支持QImageReader能识别的格式部分工具导出的 jpg 带 CMYK 色彩空间Qt 显示会异常。第三最隐蔽的坑——图片文件放在构建目录之外qrc里写的是相对路径而 Qt Creator 重新构建时没有重新运行rcc编译资源。解决先用绝对路径测试图片能否加载排除图片本身的问题再检查qrc文件是否被.pro里的RESOURCES引用最后用Qt Creator的「构建」→「重新构建项目」强制刷新资源编译。血泪经验是改完qrc后如果没重新构建rcc生成的头文件不会更新程序永远加载旧资源。4.2 中文乱码界面文字变成问号或菱形现象窗口标题、按钮文字全部显示成??????或菱形块。原因项目文件保存格式是 UTF-8但编译器默认按本地代码页Windows 下是 GBK解析源码字符串字面量。Qt5 里QString::fromUtf8()是标准做法但很多老工程图省事直接写QString(中文)这时候用的是QString::fromLocal8Bit()跟源码编码不一致就会乱码。解决统一所有源文件编码为 UTF-8并在.pro里加上QMAKE_CXXFLAGS -finput-charsetUTF-8 -fexec-charsetUTF-8MinGW 编译器会按这个参数解析源码里的字符串常量。如果不想动工程配置把中文全部换成英文也是保底方案课程设计答辩完全够用。4.3 信号槽连接失败点击地鼠无反应现象游戏能跑地鼠也冒出来了但鼠标点上去一点反应都没有计分不变。原因connect函数里信号和槽的签名不匹配。Qt5 新语法要求在编译期检查函数指针如果信号带了int参数槽函数参数类型对不上比如写成const int或反过来连接直接失败但运行时只在控制台打印一行警告弹窗看不出来。解决检查连接语句确保信号和槽的参数类型完全一致。写代码时统一用MyItem::beaten这种新语法编译器能在编译阶段查出问题不要用字符串形式的旧语法。4.4 坐标混乱地鼠跑出洞口或点击错位现象地鼠图元的位置跟背景图上的洞口对不上或者点击肉眼可见的地鼠却判定无效。原因setPos设置的是图元在场景坐标系中的位置而boundingRect返回的是图元自身的局部坐标范围。如果你在paint()里画的图片尺寸跟boundingRect()不一致Qt 的碰撞检测和事件派发会拿错误的矩形做计算。另外setPos如果没考虑图片本身的偏移图元中心跟洞口中心就对不齐。解决设计时固定洞口尺寸常量boundingRect()返回的实际尺寸跟图片尺寸严格一致。setPos时把洞口的行列坐标换算成像素坐标时加上偏移修正。一般是先算左上角坐标再把图元 size 的一半补进去做居中。5. 改造升级难度递增与计分反馈的实操技巧游戏能跑、能交作业这只是及格线。如果你想冲高分或者答辩时让老师眼前一亮改造方向其实很清晰增加动态难度、完善计分反馈、加上音效和连击特效。下面针对三个改动最小的方向给出具体实现。5.1 用定时器周期递减实现难度递增打地鼠的爽感来源于节奏变化。固定时间间隔出洞太低幼应该让地鼠的停留时间随着游戏进行逐步缩短。改动集中在handler类里void Handler::decreaseInterval() { if (m_interval 300) { m_interval - 50; m_timer-setInterval(m_interval); } }在onItemBeaten槽函数里调用decreaseInterval()每打中一只地鼠就把定时器间隔缩短 50 毫秒下限设 300 毫秒防止游戏变得不可玩。这样玩家会明显感觉到节奏在加快紧张感自然提升。5.2 计分反馈分数飘字与连击提示单一的数字变化不够直观加分的时候在洞口位置弹一个向上飘的金色数字效果立刻不同。实现方式是在myscene里临时创建一个文本图元动画结束再销毁void MyScene::showScoreTip(const QPointF pos, int score) { QGraphicsTextItem *tip addText(QString(%1).arg(score)); tip-setPos(pos); tip-setDefaultTextColor(Qt::yellow); tip-setFont(QFont(Arial, 20, QFont::Bold)); QPropertyAnimation *anim new QPropertyAnimation(tip, pos, this); anim-setStartValue(pos); anim-setEndValue(pos QPointF(0, -40)); anim-setDuration(500); connect(anim, QPropertyAnimation::finished, tip, QGraphicsTextItem::deleteLater); anim-start(QPropertyAnimation::DeleteWhenStopped); }QPropertyAnimation是 Qt 动画框架里最常用的类setStartValue和setEndValue定义位移动画轨迹finished信号触发deleteLater自动清理。注意在myscene.h里要加#include QGraphicsTextItem和#include QPropertyAnimation漏了会编译报错。5.3 游戏结束判断与排行榜更进阶一点加一个 60 秒倒计时时间到弹出QMessageBox显示最终得分再用QSettings存历史最高分。这部分代码量不大但能展示你对 Qt 事件循环和持久化的理解是答辩时的加分项。void MainWindow::onTimerTimeout() { m_timeLeft--; ui-timeLabel-setText(QString(剩余时间: %1s).arg(m_timeLeft)); if (m_timeLeft 0) { m_timer-stop(); QSettings settings(MyGame, WhackAMole); int best settings.value(bestScore, 0).toInt(); if (m_score best) { settings.setValue(bestScore, m_score); } QMessageBox::information(this, 游戏结束, QString(得分: %1\n最高分: %2).arg(m_score).arg(best)); } }QSettings在 Windows 下默认写入注册表在 Linux 下写入配置文件跨平台不用自己管路径。这里用arg(m_score).arg(best)做参数替换比字符串拼接更安全也避免了中文乱码的风险。坦白讲这套源码我拆下来最大的感受是它的「工程感」很正——不炫技每个类职责单一注释也足够多。如果你只是交作业原封不动跑起来改个主题色就能用如果你想在答辩里多讲两分钟把难度递增和计分反馈按上面的思路改进去能聊的东西会多出不少。从那以后我每拿到一个 Qt 课程设计资源都会先花十分钟理清场景、图元和 handler 的三角关系再动手改代码这个习惯帮我少踩了无数个编译和资源加载的坑。希望这份拆解也能帮到你。本文还有配套的精品资源点击获取