新闻详情

新闻详情

首页 / 资讯中心 / 详情

基于Qt的2048小游戏开发实战:C++大作业全流程解析

发布时间:2026/9/28 13:17:36来源:尧图网络
基于Qt的2048小游戏开发实战:C++大作业全流程解析
简介一套适用于高校C程序设计课程大作业的2048小游戏完整方案基于Qt框架开发面向刚开始接触桌面应用与二维数组逻辑处理的编程学习者。项目把4×4棋盘抽象为int[4][4]围绕初始化清空并置零得分、随机生成数字2并检测空格、无空格时判断游戏结束、paintEvent自绘棋盘等核心函数逐层实现开发环境为Visual Studio 2017与Qt Creator 4.3.0代码量精简便于边读边跑验证游戏流程。压缩包共7个文件主要包括2个cpp源文件、1个头文件、1个ui界面文件、1个pro工程文件、1个pro.user工程配置以及docx格式项目报告整体仅45KB按工程、代码、界面、文档组织清楚能快速定位修改位置。已有332人学习适合作为C大作业提交方案也可作为Qt小游戏开发、事件绘图机制和课程设计报告撰写的参考对于报告中功能设计与流程说明同样有直观参照价值。1. 这门大作业真正在考什么从QT 2048看C课程设计的及格线与优秀线高级语言程序设计C大作业里基于QT实现的2048小游戏是出现频率相当高的一个选题规则直观、界面工作量可控还能把C的类设计、数组操作、QT的事件循环和绘图机制全部串起来。很多同学误以为它只是“一个图形版2048”真正动手才发现方向键没反应、合并规则写错、Qt库版本报错每一步都能卡半天。这篇笔记按我实际交付同类项目的顺序展开从环境搭建、核心算法、界面绘制到项目报告把一个能交、能讲、能过答辩的完整链路拆给你。新手可以照着一步步复现有经验的也能对比一下自己的设计取舍。2. 把环境与项目骨架一次搭对QT 5.15.2安装选型、.pro工程与三个文件的分工2.1 工具链选型为什么大作业选QT Creator MinGW而不是MSVC先讲选型因为这一关卡住的人最多。网上很多“qt下载安装、qt creator配置”教程但很多人装完QT后发现事情没这么简单——你面对的不是一个软件而是一整套工具链不同版本的Qt库、不同套件Kit、附带的不同编译器。课程大作业场景下我一般建议用Qt 5.15.2的MinGW 64-bit套件配合QT Creator使用。这个组合在Windows上开箱即用编译器、调试器、qmake都在套件里配好不需要额外安装Visual Studio Build Tools。原因很现实MSVC套件虽然性能和Windows原生调试体验更好但不少同学的机器只装了QT没有VS用MSVC套件编译时会报找不到c1.exe、Windows SDK头文件缺失这一类问题折腾半天还没开始写代码就泄气了。MinGW套件自带GCC编译器点开直接能编译这也是课程设计最常见的配置。套件组合编译器适合场景常见翻车点Qt 5.15.2 MinGW 64-bitg课程大作业、跨平台演示较少Qt 5.15.2 MSVC 2019MSVC需要Windows独有API、性能敏感缺VS Build Tools时编译不过Qt 6.x MinGWg新项目、想用Qt6特性老教程不适用部分API有变动选型时还要注意版本混用问题。如果你从网上下载别人的2048工程对方用MSVC编译过你拿MinGW工具链去编译就会看到著名的报错fatal: cannot mix incompatible Qt library (version ex50601) with this librar。这不是代码逻辑问题是工具链ABI不匹配解决办法是统一套件后全量重建后面避坑章节会展开说。2.2 用.pro文件搭起最简工程骨架一个目录、三个类、一条构建命令不管用QT Creator还是命令行qmake工程的入口都是.pro文件。一个最简的2048工程长这样2048/ ├── 2048.pro ├── main.cpp ├── mainwindow.h ├── mainwindow.cpp ├── gameboard.h ├── gameboard.cpp └── report/ # 项目报告与截图2048.pro文件内容如下QT core gui widgets CONFIG c11 TARGET 2048 TEMPLATE app SOURCES \ main.cpp \ mainwindow.cpp \ gameboard.cpp HEADERS \ mainwindow.h \ gameboard.h逻辑说明QT core gui widgets决定链接哪些Qt模块。QApplication、QWidget、QPainter都在QtWidgets模块里不加这一行会直接报找不到头文件core和gui是默认模块写上只是让工程更明确。CONFIG c11开启C11语法后面代码里会用auto、范围for、lambda不开编译不过。TARGET 2048决定生成的可执行文件名。HEADERS里列出所有自定义头文件qmake靠这一项触发moc预处理漏写会导致链接错误这个坑后面细说。写好后在QT Creator里打开.pro点左下角绿色三角即可构建运行。命令行方式则是mkdir build cd build qmake ../2048.pro make ./2048参数说明qmake根据.pro生成Makefilemake负责编译和链接。单独建build目录是为了不污染源码目录这也是我推荐的干净习惯。如果你用QT Creator默认的shadow build其实就是这个机制区别只是构建目录被自动放到了工程外的临时位置。2.3 类划分GameBoard管逻辑MainWindow管界面main只做启动这是整个项目最值得花时间的设计决定。很多同学把所有代码写进MainWindow一个类里棋盘数组、移动逻辑、绘制代码、按钮响应全堆在一起。功能能跑但报告很难写答辩时老师问“移动算法怎么测的”你只能答“跑起来看效果”这是明显的减分项。我建议拆成三个部分职责分离main.cpp创建QApplication显示主窗口进入事件循环。GameBoard类只管4x4棋盘数据、移动合并、随机数生成、胜负判定完全不感知界面存在。MainWindow类继承QWidget持有GameBoard对象负责绘制、键盘事件、分数显示。main.cpp的写法是标准模板#include QApplication #include mainwindow.h int main(int argc, char *argv[]) { QApplication app(argc, argv); MainWindow w; w.show(); return app.exec(); }逻辑说明QApplication是QT程序的必需品它创建并管理事件循环之后所有鼠标、键盘、重绘消息都由这个循环分发。app.exec()会一直运行直到窗口关闭返回值作为进程退出码。MainWindow w; w.show();必须在exec之前执行否则窗口不会显示。参数argc、argv是命令行参数QT内部会解析一部分比如高分屏缩放相关参数。把GameBoard和MainWindow分开还有一个实际好处可以绕过界面直接测试核心逻辑。比如在main函数里临时写一段代码手动调move函数断点查看棋盘数组变化比每次按键看界面快得多。这个习惯能为后面报告里的“测试记录”章节积累素材也是调试阶段省时间的关键。3. 2048核心算法不写对界面全是白费棋盘、行合并、随机数与终局判定3.1 棋盘数据结构为什么用int[4][4]加merged标记而不是二维QVector2048的核心状态就是一个4x4的数字矩阵外加一个分数。数据结构的选择直接决定后面算法的复杂度和出bug的概率。常见做法有两种用QVectorQVectorint动态二维数组或者用C原生二维数组int board[4][4]。我推荐后者理由有三棋盘尺寸固定原生数组不需要动态分配memcpy可以整体备份、比较方便判断棋盘是否变化界面绘制时取数连续性能更好。动态二维数组在行列访问时多一次间接寻址代码也更啰嗦。还有一个关键辅助数组bool merged[4][4]它标记“本次滑动中某个格子是否已经参与过合并”。2048的规则里一次移动中一个数字只能合并一次。比如一行是[2,2,4]向左移动后应该得到[4,4,0,0]而不是[8,0,0,0]。如果没有merged标记只靠“相邻相等就合并”的简单循环很容易把刚合并出来的4又拿去和后面的4合并这是新手最常见的逻辑翻车点。GameBoard的头文件可以这样声明class GameBoard { public: GameBoard(); void reset(); // 核心移动接口返回棋盘是否发生了变化 bool move(int dx, int dy); // 是否还有可移动的空格或相邻相同数字 bool canMove() const; int score() const { return m_score; } const int (board() const)[4][4] { return m_board; } private: void addRandomTile(); // 在空位随机生成2或4 int m_board[4][4]; bool m_merged[4][4]; int m_score; };参数说明move接收两个方向向量dx、dy取值为-1、0、1比如向左传(-1, 0)向下传(0, 1)。返回值表示本次滑动有没有实际改动棋盘界面层靠它决定要不要生成新数字和重绘。const int ()[4][4]这种写法是对二维数组的常量引用返回board数组但不产生拷贝。如果觉得这个语法费解也可以改为返回const int*并同时传出维度但讲解这个引用语法在答辩时反而是加分点。3.2 行合并算法用“过滤、合并、补零”三步写清一个方向移动的核心是行合并。最简单清晰的实现方式不是直接在二维数组上操作而是把每一行或每一列抽成一个QVectorint处理完再写回去。以向左移动为例一行数字的合并分成三步第一步去掉所有0保留非零数字第二步从左到右扫描相邻相等就合并合并过的数字跳过第三步在后面补0凑满4个。这段代码是GameBoard的私有辅助函数void GameBoard::mergeLine(QVectorint line) { // 第一步过滤掉0 QVectorint nums; for (int v : line) if (v ! 0) nums.append(v); // 第二步合并相邻相等数字 QVectorint res; for (int i 0; i nums.size(); i) { if (i 1 nums.size() nums[i] nums[i 1]) { res.append(nums[i] * 2); m_score nums[i] * 2; i; // 跳过被合并的第二个数字 } else { res.append(nums[i]); } } // 第三步补0到4个 while (res.size() 4) res.append(0); line res; }逻辑说明最关键的代码是合并循环里的i。当发现nums[i]和nums[i1]相等时把两倍值放入结果然后i自增一次跳过后一个数字防止它被再次合并。漏掉这一步是2048合并逻辑最大的bug来源。m_score在这里同步累加每次合并加上生成格子的数值也就是两倍值。注意mergeLine假定调用前已经备份过棋盘。如果mergeLine内部出错提前返回外层m_score和棋盘状态会不一致所以这个函数内部不要写多余的提前return分支。有了mergeLine四个方向各写一层循环即可核心合并逻辑只维护一份。左移和上移的写法几乎是镜像的// 左移逐行处理 for (int r 0; r 4; r) { QVectorint line; for (int c 0; c 4; c) line.append(m_board[r][c]); mergeLine(line); for (int c 0; c 4; c) m_board[r][c] line[c]; } // 上移逐列处理 for (int c 0; c 4; c) { QVectorint line; for (int r 0; r 4; r) line.append(m_board[r][c]); mergeLine(line); for (int r 0; r 4; r) m_board[r][c] line[r]; }参数说明左移里内层循环固定行索引r列索引c从0到3正向取右移只要把取数和写回的顺序改成倒序下移同理对列做倒序。这样虽然四个方向各有循环但每个方向十几行代码直白好懂。另一种思路是“旋转棋盘法”——把棋盘整体旋转90度后统一做左移再旋转回去代码更优雅但旋转矩阵本身容易绕晕大作业我建议先写直白版本答辩时提一句“可以把多方向问题规约为单方向算法”就够了。move函数还需要处理“棋盘是否真的变了”这个问题。移动之前用memcpy备份旧棋盘合并结束后再比较没变化就不生成新数字返回false。否则玩家在满盘却无法移动的状态下乱按方向键棋盘会不停刷出新数字导致游戏结束判定混乱。bool GameBoard::move(int dx, int dy) { int old[4][4]; memcpy(old, m_board, sizeof(m_board)); // 按方向执行左移/右移/上移/下移的逻辑... // 统一调用 mergeLine 处理行或列 bool changed memcmp(old, m_board, sizeof(m_board)) ! 0; if (changed) addRandomTile(); return changed; }逻辑说明memcpy和memcmp在这里非常合适因为棋盘是连续4x4的int数组。changed是后续界面层决定是否重绘的唯一依据也顺便拦截了“无效操作”。这里不需要自己遍历比较标准库函数一次搞定。3.3 随机数与终局判定QRandomGenerator怎么用才算专业新数字的生成逻辑很直接先从棋盘收集所有空位随机挑一个90%概率放210%概率放4。实现上我建议直接用QT自带的QRandomGenerator而不是C风格的rand()void GameBoard::addRandomTile() { // 收集所有空位置 QVectorQPairint, int empty; for (int r 0; r 4; r) for (int c 0; c 4; c) if (m_board[r][c] 0) empty.append({r, c}); if (empty.isEmpty()) return; // 随机选择空位 int idx QRandomGenerator::global()-bounded(empty.size()); int r empty[idx].first; int c empty[idx].second; // 90%生成210%生成4 m_board[r][c] (QRandomGenerator::global()-bounded(10) 9) ? 2 : 4; }参数说明bounded(n)返回0到n-1之间的整数是QRandomGenerator最常用的接口。为什么不用rand()rand()需要手动srand设置种子而且很多人用的srand(time(NULL))在快速连续调用时会拿到相同种子序列QRandomGenerator::global()是线程安全的全局实例无需管理种子也是现代C更推荐的做法。答辩被问“随机数怎么保证公平”答这句就够。终局判定是另一段独立逻辑只要满足两个条件之一游戏就还能继续棋盘上有空格或者存在任意两个相邻数字相等。代码如下bool GameBoard::canMove() const { for (int r 0; r 4; r) { for (int c 0; c 4; c) { if (m_board[r][c] 0) return true; if (c 1 4 m_board[r][c] m_board[r][c 1]) return true; if (r 1 4 m_board[r][c] m_board[r 1][c]) return true; } } return false; }逻辑说明边界检查c 1 4和r 1 4是必须的否则在右边界或下边界访问数组会越界属于未定义行为轻则读到垃圾值重则随机崩溃。这个函数每次移动后调用一次即可不需要轮询。当所有格子都填满且任何方向都没有相邻相等数字就是死局这时界面层弹一个QMessageBox提示收尾。4. 用QPainter把棋盘画出来重绘机制、方向键事件与分数持久化4.1 界面方案对比QPainter手绘棋盘还是用QLabel拼格子界面层是大部分同学纠结最久的地方。网上能看到的QT 2048界面实现基本分成两派一是用QT Designer拖控件用QGridLayout拼16个QLabel二是自定义QWidget重写paintEvent用QPainter手绘。这两种我都见过交上来的作业结果是手绘派普遍更能拿高分。原因很现实QPainter方案对颜色、间距、字体有完全控制动画扩展方便QLabel拼格子方案代码看似简单但每个格子更新要单独setText、setStyleSheet16个控件状态同步很容易乱。对比项QPainter手绘QGridLayout QLabel代码量30行左右控件创建多更新代码散动画扩展容易加位移插值基本做不了平滑移动答辩展示可讲坐标计算、抗锯齿、字体适配可讲内容少适合场景大作业加分快速原型我一般直接走QPainter手绘路线。核心代码在MainWindow的paintEvent里void MainWindow::paintEvent(QPaintEvent *) { QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); const int cellSize 100; const int gap 10; const int margin 20; for (int r 0; r 4; r) { for (int c 0; c 4; c) { int value m_game-board()[r][c]; QRect rect(margin c * (cellSize gap), margin r * (cellSize gap), cellSize, cellSize); painter.fillRect(rect, colorForValue(value)); if (value ! 0) { painter.setPen(Qt::white); QFont font painter.font(); font.setPointSize(value 100 ? 28 : 20); painter.setFont(font); painter.drawText(rect, Qt::AlignCenter, QString::number(value)); } } } }逻辑说明整个绘制就是两个嵌套循环外层行内层列每个格子根据行列算出它在窗口中的矩形位置先fillRect填充背景色再在格子中央drawText画出数字。坐标公式margin c * (cellSize gap)是固定套路margin是棋盘外留白gap是格子间距c是列索引。这样画出来的棋盘格子整齐、间距一致。参数说明cellSize、gap、margin三个常量决定了整个棋盘观感都集中在paintEvent开头想调整尺寸改这三处即可。colorForValue是自己写的私有函数返回QColor。典型做法是维护一个映射2用浅黄、4用橘黄、8用橙红、16以上颜色加深2048用深红或暗棕。字体大小按位数调整两位数时用较小的字号避免被格子边框裁掉。QPainter::Antialiasing是抗锯齿选项画矩形和文字更平滑开了之后在2K屏上不会出现边缘锯齿。需要说明的是如果你的课程强制要求用qt designer界面设计也不是不能混搭用QT Designer拖出分数标签和“重新开始”按钮棋盘区域放一个空的QWidget然后右键这个空控件选择“提升为”自定义类指定成你的BoardWidget。外部控件交给Designer核心棋盘还是手绘这样两边都不耽误。4.2 键盘事件与重绘刷新keyPressEvent的正确打开方式键盘响应是QT事件机制中最直接的入口。在MainWindow里重写keyPressEvent判断按键是方向键就把方向向量传给GameBoard的move函数根据返回值决定是否重绘void MainWindow::keyPressEvent(QKeyEvent *event) { bool moved false; switch (event-key()) { case Qt::Key_Up: moved m_game-move(0, -1); break; case Qt::Key_Down: moved m_game-move(0, 1); break; case Qt::Key_Left: moved m_game-move(-1, 0); break; case Qt::Key_Right: moved m_game-move(1, 0); break; default: QWidget::keyPressEvent(event); return; } if (moved) { if (!m_game-canMove()) { // 游戏结束弹提示或显示结束状态 } update(); // 请求重绘不直接调用paintEvent } }逻辑说明move返回true说明棋盘发生变化界面要做两件事检查是否还能继续以及调用update()刷新画面。很多新手在这个位置直接调用paintEvent或者自己写循环重绘这是错误做法。update()是异步的QT会把重绘请求合并进事件循环短时间内多次调用不会造成重复绘制这是QT绘图机制的核心经验。还有一个细节极易翻车MainWindow里如果放了按钮控件比如“重新开始”键盘事件会被按钮抢走。解决方法有两个层面。第一MainWindow构造函数里调用setFocusPolicy(Qt::StrongFocus)让主窗口能够接受键盘焦点第二按钮点击后手动执行setFocus()把焦点还回主窗口。如果你的界面控件很多更彻底的办法是重写eventFilter统一拦截按键但大作业用setFocus就够了。游戏结束的判定要放在move之后而不是之前移动可能填满最后一个空格也可能把局面推成死局所以正确顺序是“先移动、再判断、最后提示”。弹窗可以用QMessageBox也可以在窗口底部画一行状态文字后者更有设计感但需要额外维护一个状态变量。4.3 分数、最高分与“再来一局”状态管理与持久化小技巧分数和最高分是2048完整度的重要标志也是报告里功能列表能多写两页的地方。分数来源很简单GameBoard在mergeLine里已经累加了m_scoreMainWindow只需要在每次移动后刷新界面上的分数显示。最高分则需要持久化否则关掉程序就丢这门大作业的完整度会打折扣。我的做法是用QSettingsvoid MainWindow::updateBestScore() { QSettings settings(MyUniversity, 2048); int best settings.value(best, 0).toInt(); int current m_game-score(); if (current best) { settings.setValue(best, current); best current; } // 更新界面上的最高分标签 }参数说明QSettings构造函数前两个参数分别是组织名和应用名在Windows上写入注册表Linux上写配置文件你不用关心具体路径QT会自己处理。settings.value(best, 0)读取键名为best的值不存在时返回默认0。用QSettings而不是自己写文件省去文件路径、读写权限、格式化的一堆麻烦也是QT官方推荐的做法。答辩被问“最高分存哪里”你可以直接说出存储机制这一步很加分。“再来一局”按钮的逻辑更简单调用m_game-reset()重置棋盘和分数清空merged标记和界面上所有状态重新生成两个初始数字然后update()。注意reset里如果忘记重置m_merged会导致重新开局后第一次移动的合并行为完全错乱——这是隐藏得很深的小坑。如果想让界面更完整可以在窗口标题栏动态显示分数setWindowTitle(QString(2048 - 分数: %1).arg(score))这种方式不需要额外布局标签也是很多开源版本的做法。升级方向还可以加“撤回上一步”“步数统计”“移动动画”但大作业不必贪多把基础功能做扎实更划算动画那些可以作为报告里的“后期改进方向”。5. 避坑QT 2048最容易翻车的五个细节与排查步骤写到这里把实战中反复出现的高频坑集中说一遍。按现象、原因、解决三件套展开省得你遇到问题时在搜索页里来回翻。5.1 构建与运行期的报错版本混用、平台插件与moc预处理现象一编译能过但链接或启动时报fatal: cannot mix incompatible Qt library (version ex50601) with this librar或者类似找不到Qt库的弹窗。原因Qt库和编译器工具链不匹配。最常见场景是你下载了别人的工程对方用MSVC套件编译过你在自己的MinGW套件里直接打开GCC链接器拿MinGW格式的路径去链接MSVC格式的Qt库版本和ABI全对不上。第二种场景是同一台机器装了多个Qt版本QT Creator自动选择的套件和你期望的版本不一致。另外如果头文件里加了Q_OBJECT宏但.pro里的HEADERS漏写了这个头文件也会在链接阶段报vtable错误这属于moc预处理没有跑起来的典型症状。解决统一编译器套件。打开QT Creator的Projects面板确认构建套件选的是Qt 5.15.2 MinGW 64-bit然后执行“构建 - 清理”再“构建 - 重新构建”彻底清掉旧的shadow build目录。只点“重新构建”有时不够旧moc产物和新库版本冲突清理才能根治。手动命令行构建时记得改完.pro一定要重新跑一遍qmake再make不要直接make。现象二在Linux或嵌入式板子比如树莓派上运行终端输出qt.qpa.plugin: Could not find the Qt platform plugin linuxfb in ...窗口起不来。原因Qt显示依赖“平台插件”。桌面Linux默认用xcb或wayland插件嵌入式场景才会指定linuxfb。出现这个报错要么是环境变量QT_QPA_PLATFORM被设成了linuxfb要么是plugins/platforms目录不在Qt的搜索路径里。解决先执行echo $QT_QPA_PLATFORM查看环境变量如果被错误设置执行export QT_QPA_PLATFORMxcb再运行。如果插件目录缺失把Qt安装目录下plugins/platforms整个目录复制到可执行文件旁边的platforms子目录。这个坑Windows上不出现但做树莓派交叉编译Qt时极其常见搜这个报错时能看到一堆同类血泪经验。5.2 运行期的逻辑与界面翻车焦点丢失、事件循环阻塞与高分屏模糊现象三程序跑起来鼠标点按钮正常但按方向键完全没反应。原因键盘事件没有到达MainWindow。QT里键盘事件默认发给当前获得焦点的控件。如果你在窗口里放了“重新开始”按钮启动时焦点可能落在按钮上按键事件被按钮消费MainWindow的keyPressEvent根本没被触发。而且按钮被点击后焦点不会自动回到主窗口。解决MainWindow构造函数里调用setFocusPolicy(Qt::StrongFocus)确保主窗口能接受键盘焦点按钮的clicked信号里处理完业务后执行this-setFocus()把焦点抢回来。大作业用这两步就够不需要上eventFilter。现象四某个方向按键后窗口卡死白屏无法关闭。原因事件循环被阻塞。常见于有人在move函数里为了做“逐格移动”动画加了QThread::sleep()或while忙等也有的是canMove的多重循环里边界写错导致死循环。Qt是事件驱动模型界面刷新依赖事件循环持续转动一旦某段代码在事件处理里同步阻塞整个窗口就失去响应。解决动画不要用阻塞循环。我的建议是先不做逐格动画移动后直接重绘新状态如果确实要平滑效果用QTimer定时器每16ms更新一次偏移量并调用update()或者用QVariantAnimation让动画过程在事件循环的间隙推进。排查卡死时先在move函数开头和结尾加qDebug打印确认是否进入死循环配合调试器暂停查看调用栈是定位最快的路径。现象五高分屏下棋盘小、字体糊窗口拉伸后棋盘不变。原因两个原因叠加。其一是paintEvent里硬编码cellSize100窗口放大后格子相对窗口显得越来越小其二是没有开启高分屏缩放在2K、4K分辨率的Windows上Qt默认按100%逻辑分辨率绘制字自然就糊。解决main.cpp里在创建QApplication之前加一行QApplication::setAttribute(Qt::AA_EnableHighDpiScaling, true);Qt 6之后默认开启老版本必须手动加。cellSize不要写死在paintEvent开头根据窗口宽度动态计算int cellSize (width() - 2 * margin - 3 * gap) / 4;这样窗口任意拉伸棋盘自动跟随观感改善很明显。如果你用QT Designer设计了布局同样要把布局策略设为Expanding否则窗口拉伸控件不动。这五条里现象一和现象三占了此类项目答疑量的一大半基本是环境问题和焦点问题不是算法问题。动手之前先统一工具链写界面之前先想好焦点策略能少走很多弯路。6. 项目报告别写成代码附录答辩追问、测试记录与三个加分截图报告是这个标题的另一半——“源代码项目报告”。很多人的报告就是每个文件代码粘一遍老师翻两页就失去兴趣。我一般这样组织报告结构清晰而且不用写太多废话章节内容建议篇幅需求分析游戏规则、功能清单1页总体设计类图、GameBoard与MainWindow职责划分1-2页详细设计行合并算法、merged标记、随机数策略2-3页测试与运行手动测试用例表、运行截图1-2页总结遇到的问题、改进方向1页测试记录是很多人忽略但答辩老师一定找的部分。不需要自动化框架手动测试用例表就行列“操作、预期结果、实际结果、是否通过”四列覆盖左移合并、右移边界、无法移动时不刷数字、分数累加等场景比写三千字功能描述更有说服力。答辩追问环节高频问题其实就三个一次移动如何避免同一数字被重复合并答案指向merged标记或合并循环里的i新数字为什么是2和4答案指向bounded(10) 9的概率控制四个方向如何共用同一条合并逻辑答案指向行列抽取的统一循环。能为这三个问题准备好代码定位和一句话解释答辩基本稳了。报告里放三张截图最有价值初始界面、游戏进行中、游戏结束提示每张图配两行说明放在测试章节里当证据。最后说个我自己的教训我第一次交类似大作业时截图是最后几天集中补的老师看完反馈说“看不出开发过程”。后来再写这类项目报告每完成一个功能就截一张图、更新一行测试记录到交报告时材料自然攒齐过程感也有了答辩底气完全不同。截图这种素材边写代码边积累最省力。希望帮到你。本文还有配套的精品资源点击获取
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

2.8 配置项:constants/ 全局常量体系与 TaoToken 统一 Key 接入实践 2026/9/28 18:22:58

2.8 配置项:constants/ 全局常量体系与 TaoToken 统一 Key 接入实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
智能旅行助手Agent实战:用TaoToken统一Key打通前后端分离的多Agent系统 2026/9/28 18:22:58

智能旅行助手Agent实战:用TaoToken统一Key打通前后端分离的多Agent系统

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
UltraEditor 替换公式正则表达式:用 TaoToken 统一 Key 打通 AI 辅助批量改写 2026/9/28 18:22:58

UltraEditor 替换公式正则表达式:用 TaoToken 统一 Key 打通 AI 辅助批量改写

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
Claude Code 解析:从 Agent Loop 到 QueryEngine 的配置骨架 2026/9/28 18:22:58

Claude Code 解析:从 Agent Loop 到 QueryEngine 的配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
【C语言/数据结构】零基础打造控制台游戏:贪吃蛇实战教程----链表与Win32 API的完美结合! 2026/9/28 18:22:58

【C语言/数据结构】零基础打造控制台游戏:贪吃蛇实战教程----链表与Win32 API的完美结合!

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

阅读更多 →
基于 LangChain4j + LangGraph4j 的 Java 低代码智能体平台架构落地实践 2026/9/28 18:22:52

基于 LangChain4j + LangGraph4j 的 Java 低代码智能体平台架构落地实践

在 Java 生态里做 AI 应用开发,这两年有一个特别明显的感受:LangChain4j 和 LangGraph4j 的组合,正在把“智能体开发”从纯手工编码推向下一个阶段。我见过太多团队在同一个问题上反复踩坑——模型调用、记忆管理、工具编排、状态流转&#x…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉