简介:本资源为基于QT与C++开发的宠物小精灵人机对战游戏完整项目,面向计算机、通信、人工智能、自动化等专业的学生与教师,可用于毕业设计、期末课程设计或课程大作业,也适合C++与Qt初学者进阶练手。压缩包共36个文件,约1.88MB,以cpp与h源码文件为主,配合ui界面文件、qrc资源文件、pro工程文件及png图片素材,并附有md格式文档说明,覆盖客户端与服务端两套工程结构。项目围绕宠物小精灵对战逻辑展开,包含玩家、精灵基类、数据库等模块,代码经过调试测试可正常运行,答辩评审分达98分。已有99人学习下载,读者可借此理解Qt界面搭建、C++面向对象设计与人机对战算法实现,并在此基础上修改调整以扩展新功能。
1. 从一份 98 分课设说起:QT+C++ 宠物小精灵人机对战到底能跑出什么
如果你正在找一份能直接编译、能讲清架构、还能在答辩时扛住追问的 C++ 课程设计,那这套基于 QT 的宠物小精灵人机对战项目值得先跑一遍再决定要不要改。它不是那种只丢几个 cpp 文件的半成品,而是拆成了problem3_server和problem3_client两个工程,带mainwindow.ui、image.qrc、database.cpp、pokemon.cpp、player.cpp等完整模块,还配了readme.md和登录、战斗、失败三张截图。换句话说,它把「界面—数据—战斗逻辑—资源加载」这条链路都摆出来了,适合计算机、通信、自动化方向的同学拿来做期末大作业或毕业设计底稿。下面我按实际拆包顺序,把编译、运行、改参数和常见翻车点一次讲透。
2. 拆包先看工程结构:server/client 双工程与 QT 模块依赖
2.1 两个 .pro 文件决定了你该怎么打开
拿到压缩包后别急着双击main.cpp,先看根目录下有两个工程文件:problem3_server.pro和problem3_client.pro。这说明项目不是单工程,而是按「服务端逻辑 + 客户端界面」拆开的。常见做法是分别用 QT Creator 打开两个.pro,各自构建。如果你只打开其中一个,另一个的mainwindow.cpp和pokemon.cpp不会自动进编译队列,运行起来就会缺符号。
从文件清单看,两边都带了mainwindow.h/.cpp/.ui、definition.h、pokemon.h/.cpp、player.h/.cpp、pokemonbase.h/.cpp,说明战斗实体和玩家数据是双端各自持有一份。这种结构在课设里很典型:客户端负责画界面和收输入,服务端负责算伤害和判胜负。你改数值时要注意两边是否同步,否则会出现「界面显示扣血了,服务端判定还没死」的玄学现象。
2.2 QT 版本与 C++ 标准怎么选
项目里出现了res.qrc.autosave和image.qrc,说明图片资源走的是 QT 资源系统。这类工程一般用 QT 5.x 的qmake构建最稳,QT 6 也能编,但QRegExp、QString::split等老 API 可能需要微调。我一般会先确认三件事:
| 检查项 | 推荐值 | 原因 |
|---|---|---|
| QT 版本 | 5.12 / 5.15 | 与多数课设模板兼容,ui文件不易报错 |
| 编译器 | MinGW 7.3 或 MSVC 2017 | 避免c++17特性缺失 |
| C++ 标准 | C++11 | 项目未用高版本特性,降标准反而少报错 |
在 QT Creator 里打开.pro后,点「Projects」→「Build」→「C++ Standard」选C++11。如果直接默认 C++17,有些老写法会触发register关键字移除的警告,虽然不一定报错,但答辩时被问到会显得没调过。
2.3 资源文件与数据库文件的加载顺序
image.qrc和res.qrc负责把图片编进可执行文件,database.cpp负责数据持久化。常见坑是:先qmake再build,但qrc没被重新生成,导致运行时报「QPixmap: Cannot load image」。解决方法是右键.qrc文件选「Open in Editor」确认图片路径存在,然后执行一次qmake,再Rebuild。
数据库部分,database.cpp里通常用QSqlDatabase::addDatabase("QSQLITE")。如果你机器上没装 SQLite 驱动,运行时会打印「QSqlDatabase: QSQLITE driver not loaded」。这时候不用重装 QT,去 QT 安装目录的plugins/sqldrivers下确认有没有qsqlite.dll,没有就补上,或者把数据库操作临时改成读写本地文件,先让主流程跑通。
3. 编译与运行:从 qmake 到登录界面的完整链路
3.1 用 QT Creator 构建 server 与 client
先打开problem3_server.pro,选好构建套件,点左下角锤子图标。第一次构建会生成Makefile和ui_mainwindow.h。如果报「cannot find -lproblem3_server」,说明.pro里TARGET名字和输出目录对不上,检查DESTDIR是否为空。
构建通过后,再打开problem3_client.pro做同样操作。两个工程都编好后,先运行 server,再运行 client。如果 client 连不上,看mainwindow.cpp里有没有QTcpSocket::connectToHost调用,确认端口号和 server 监听端口一致。课设里常见端口是8888或12345,不一致就改 client 的connectToHost("127.0.0.1", 端口)。
// mainwindow.cpp 中客户端连接服务端的典型写法 QTcpSocket *socket = new QTcpSocket(this); socket->connectToHost("127.0.0.1", 8888); // 端口需与 server 监听一致 if (!socket->waitForConnected(3000)) { QMessageBox::warning(this, "连接失败", "请先启动服务端"); }这段代码的逻辑是:创建 socket,发起连接,等 3 秒。如果超时,弹窗提示先启动服务端。参数8888是常见课设端口,你可以在 server 的mainwindow.cpp里搜listen找到实际值。注意waitForConnected会阻塞 UI,如果不想卡界面,改成信号槽connected()和errorOccurred()异步处理。
3.2 登录界面与图片资源路径
login.png和fail.png是截图,不是运行时资源。真正加载的是image.qrc里注册的图片。如果你把可执行文件拷到别的目录,图片不会丢,因为已经编进二进制。但如果你在代码里写的是绝对路径C:/Users/.../image.png,换机器就翻车。检查mainwindow.cpp里有没有QPixmap(":/new/prefix1/xxx.png")这种带冒号的写法,冒号代表资源系统,才是正确姿势。
登录逻辑一般在mainwindow.cpp的on_loginButton_clicked()里。它会读QLineEdit的文本,和database.cpp里的用户表比对。如果数据库为空,先注册一个账号,或者看readme.md里有没有默认账号密码。没有就自己在database.cpp里加一条INSERT语句,别硬猜。
3.3 战斗界面的初始化与回合触发
进入战斗后,pokemon.cpp和player.cpp开始工作。pokemonbase.h定义了基类,pokemon.h继承它并实现具体技能。常见做法是:每个回合玩家点技能按钮,客户端把技能 ID 发给服务端,服务端算伤害后回传新血量,客户端刷新QProgressBar或QLabel。
如果你发现点了技能没反应,先看mainwindow.cpp里有没有connect(ui->skillButton, &QPushButton::clicked, this, &MainWindow::onSkillClicked)。没有连接信号槽,按钮就是摆设。再看definition.h里技能枚举和pokemon.cpp里的switch分支是否一一对应,漏一个 case 就会导致该技能无伤害。
// pokemon.cpp 中技能伤害计算的简化逻辑 int Pokemon::calcDamage(int skillId, const Pokemon &target) { int base = 0; switch (skillId) { case 1: base = 20; break; // 普通攻击 case 2: base = 35; break; // 特殊技能 case 3: base = 50; break; // 大招 default: base = 0; break; } // 属性克制:火克草,水克火,草克水 if (this->type == "fire" && target.type == "grass") base *= 2; if (this->type == "water" && target.type == "fire") base *= 2; if (this->type == "grass" && target.type == "water") base *= 2; return base; }这段代码里skillId对应按钮编号,base是基础伤害,属性克制用if判断。参数target是对方精灵,用来取属性。你可以改base数值来调整平衡,比如把大招从 50 改成 40,避免一击秒杀。注意type字符串要和definition.h里的定义完全一致,大小写错了克制就不生效。
4. 避坑与排查:编译报错、资源丢失、战斗卡死
4.1 现象:编译时报「undefined reference toPokemon::Pokemon()」
原因:pokemon.cpp没有加入.pro的SOURCES,或者构造函数声明了但没实现。解决:打开.pro文件,确认SOURCES += pokemon.cpp存在;再检查pokemon.h里的构造函数声明和pokemon.cpp里的实现签名是否一致,包括const修饰符。
4.2 现象:运行后图片不显示,按钮是空白
原因:qrc资源没编译进去,或者图片路径写错。解决:在 QT Creator 里双击image.qrc,看文件列表是否打勾;再在代码里搜QPixmap,确认路径以:/开头。如果还不行,删掉构建目录,重新qmake+Rebuild。
4.3 现象:点击技能后界面卡死,几秒后闪退
原因:在 UI 线程里做了阻塞等待,比如waitForReadyRead没设超时,或者服务端没回包。解决:把网络等待改成异步信号槽,或者加waitForReadyRead(1000)超时。另外检查database.cpp里有没有在循环里反复open数据库,那会拖慢响应。
4.4 现象:服务端和客户端血量不一致
原因:两边各自维护了一份player对象,客户端本地先扣血,服务端又扣一次。解决:只让服务端算伤害,客户端收到回包再刷新。在mainwindow.cpp里把本地扣血注释掉,改成socket->write发请求,然后在readyRead槽里更新 UI。
4.5 现象:换台电脑编译报「Qt version not found」
原因:.pro.user文件里记录了原机器的 QT 路径。解决:删掉problem3_server.pro.user和problem3_client.pro.user,重新用 QT Creator 打开.pro,它会让你重新选套件。这个文件本来就不该进版本库,删了不影响源码。
5. 进阶改法:把固定伤害改成可配置的 JSON 技能表
如果你想让这份课设看起来更有工程味,可以把pokemon.cpp里硬编码的伤害值抽成 JSON 文件,运行时加载。这样改数值不用重编译,答辩时也能讲「数据驱动」。具体做法:在项目目录建skills.json,用QJsonDocument解析,把skillId、baseDamage、typeMultiplier读进QMap。
// 加载 skills.json 并替换硬编码伤害 QFile file(":/config/skills.json"); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "技能表加载失败,回退默认值"; } else { QJsonDocument doc = QJsonDocument::fromJson(file.readAll()); QJsonObject root = doc.object(); for (auto it = root.begin(); it != root.end(); ++it) { int id = it.key().toInt(); QJsonObject skill = it.value().toObject(); m_skillTable[id] = skill["baseDamage"].toInt(); // 存入成员变量 } }这段代码先打开资源文件,失败就打印警告并走默认值,成功则遍历 JSON 对象,把技能 ID 和基础伤害存进m_skillTable。参数:/config/skills.json需要你把文件加入res.qrc,否则路径无效。改完后,calcDamage里不再写case 1: base = 20,而是base = m_skillTable.value(skillId, 10),默认值 10 防止漏配。
验证方法:改一次 JSON 里的数值,重新运行,看伤害是否变化。如果没变,检查qrc是否重新编译,或者QFile打开失败时有没有走默认分支。我一般会在加载后加一行qDebug() << m_skillTable;,确认表内容对了再继续。
从那以后我每次拿到课设源码,都先删.pro.user、确认qrc路径、再跑一遍最小连接测试,这三步走完基本不会翻车。希望帮到你。
本文还有配套的精品资源,点击获取