Qt5打地鼠游戏源码全解析:从场景框架到信号槽实战
2026/9/23 19:02:31 网站建设 项目流程

简介:面向期末大作业与课程设计场景的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.promain.cppmainwindow.cpp/hmyscene.cpp/hmyitem.cpp/hhandler.cpp/himg.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.qrc

QT += core gui声明依赖 Qt 的核心模块和 GUI 模块,greaterThan(QT_MAJOR_VERSION, 4): QT += widgets是兼容 Qt4/Qt5 的写法,Qt5 里图形控件类都挪到了 widgets 模块,这一行必须保留,否则编译直接报找不到QMainWindowTEMPLATE = 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槽函数上。这里注意ROWSCOLS是定义在头文件里的常量,改这两个数就能调整游戏难度——洞越多,玩家反应时间越短。

boundingRect()必须返回正确的矩形范围,否则点击判定会偏。常见做法是让它跟图片实际显示尺寸一致:

QRectF MyItem::boundingRect() const { return QRectF(0, 0, 100, 100); }

图元的事件处理靠重写mousePressEvent

void 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-bitQt 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="/"> <file>img/bg.png</file> <file>img/bg1.png</file> <file>img/chui.png</file> <file>img/chui_up.png</file> <file>img/chui_down.png</file> <file>img/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-charset=UTF-8 -fexec-charset=UTF-8

MinGW 编译器会按这个参数解析源码里的字符串常量。如果不想动工程配置,把中文全部换成英文也是保底方案,课程设计答辩完全够用。

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 动画框架里最常用的类,setStartValuesetEndValue定义位移动画轨迹,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 的三角关系,再动手改代码,这个习惯帮我少踩了无数个编译和资源加载的坑。希望这份拆解也能帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询