ARTICLE DETAIL

资讯详情

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

C++与Qt坦克大战实战:从零构建2D游戏的核心机制

C++与Qt坦克大战实战:从零构建2D游戏的核心机制 简介一套完整的 C/Qt 坦克大战游戏项目源码面向用 Qt 做课程设计、毕业设计或自学游戏开发的 C 学习者核心目标是借助经典坦克对战场景加深对面向对象编程的理解。压缩包共 28 个文件包含 10 个 C 源文件、10 个头文件、若干 PNG/BMP 图形素材、qrc 资源文件与 Qt 工程文件源码按坦克、子弹、地图、爆炸、状态管理等模块拆分界面、逻辑与资源分离可以直接编译运行。项目覆盖坦克移动射击、玩家与敌方坦克交互、碰撞检测、地图编辑加载、分数生命值状态切换等典型玩法并演示了类继承、多态、Qt 信号槽、QGraphicsView 渲染、定时器动画等关键技术的实际用法还预留了升级与魔法攻击等扩展思路。资源包仅 819KB轻量便于阅读目前已有 5599 人学习下载适合逐模块对照源码研习为独立开发小型 Qt 游戏打下基础。1. 项目概述与整体设计思路1.1 为什么选坦克大战作为 C/Qt 练手项目坦克大战可以说是很多人的童年回忆FC 上那款经典游戏的核心玩法不外乎三件事控制坦克移动、发射子弹打爆敌人、保护老家不被偷袭。但就是这三件事足以把一个 C 初学者进阶路上的知识几乎全部串起来。最近我花了两周时间用 C 和 Qt 把这款游戏重新写了一遍代码整理成了带完整工程结构、可以直接编译运行的版本。选择这个项目的原因很实在它不像扫雷和贪吃蛇那样过于简单也不像 RPG 那样需要复杂的资源管线。坦克大战刚好卡在中间——你要处理对象管理、碰撞检测、动画刷新、输入响应、甚至简单的 AI 逻辑同时整体代码量控制在 3000 行以内非常适合作为学习 C 面向对象编程和 Qt 图形界面的实战载体。我这次没有用 Qt Quick/QML而是选择了 Qt Widgets QPainter 自绘方案原因后面细说。核心诉求就是四个字结构清晰。网上不少教程喜欢把几百行代码全塞进一个文件里能跑但根本没法维护这种坏习惯千万别学。我把项目拆成了场景模块、坦克模块、子弹模块、地图模块、AI 模块和音效模块每个模块之间只通过接口通信这样改一个功能时不会牵连到其他部分也方便将来继续扩展。1.2 开发环境与工具链配置先交代一下我用的环境方便你复现操作系统Windows 10/11Linux 上也能跑CMake 配置不做平台判断的话基本开箱即用编译器MinGW 13.1 或 MSVC 2022 均可Qt 版本5.15.2 LTS建议用这个稳定且网上资料最多Qt 6.x 也兼容但部分写法需要微调构建工具qmake 或 CMake工程里我都配好了注意Qt 5.15 是最后一个支持 Win7 的 LTS 版本如果你在旧机器上开发选它没毛病。如果追求新特性Qt 6.5 LTS 以后的版本在绘图性能上确实有优化但坦克大战这种 2D 小游戏完全用不到。安装 Qt 的时候记得把 Qt Charts 之外的基础模块选上就行核心就是 Qt Widgets 和 Qt Multimedia音效要用。国内用户可以直接用清华、中科大的镜像站下载在线安装包速度比官方源快很多具体地址大家自己搜一下就能找到我这里就不贴了。配置好 Qt 环境后CI 工具链编译器路径会自动匹配到 qmake 和 moc。说到 moc新手第一次接触 Qt 肯定会懵——C 里明明没有信号槽这回事Qt 偏偏能编译通过靠的就是 moc 这个元对象编译器。你写的 Q_OBJECT 宏所在类的头文件会被 moc 预处理生成对应的元信息代码所以如果遇到链接错误提示 undefined reference to vtable for Tank八成就是头文件里有 Q_OBJECT 但没有重新跑 qmake 或者没有把头文件加进工程里后面我会在排查章节详聊这个问题。1.3 完整功能清单与模块划分动手写代码前一定要先画功能拆解图。我列一下这个版本的最终功能你可以当成需求文档用模块职责关键类/组件场景管理游戏主循环、渲染调度GameScene坦克对象玩家/敌人的父类与派生类Tank, PlayerTank, EnemyTank子弹系统发射、飞行、命中判定Bullet地图系统墙体、砖块、钢块、河流、出生点GameMap, WallAI 系统敌人移动与攻击决策AIController界面控制开始画面、暂停、分数、生命数HUD, GameWidget音效系统开火、命中、爆炸、背景音乐SoundManager模块之间通过 Qt 的信号槽机制解耦比如子弹打中坦克时Bullet 不需要知道对方是谁只要发射一个 hit 信号由 GameScene 负责处理伤害逻辑。这种陌生人模式下不直接联络由中间人协调的方式就是 Qt 信号槽最典型的应用场景也是这个项目里最值得学习的设计思路。2. Qt 的核心机制与游戏循环搭建2.1 事件循环与定时器驱动的帧循环如果没接触过 GUI 框架上来可能会被 Qt 的事件循环搞晕。一句话解释Qt 的 main 函数最后都会调用 app.exec()这个函数进去之后不会立刻返回而是进入一个无限循环不断从事件队列里取出鼠标键盘消息、定时器消息、网络消息等然后分发给对应的处理函数。很多新手以为程序是从上到下执行完就退出了实际上 GUI 程序的绝大多数逻辑都是被动的——用户点一下按钮才执行一段代码。坦克大战这种实时刷新游戏比较特殊它需要每秒钟刷新几十次画面来模拟流畅的运动所以不能只依赖被动事件。我的做法是使用 QTimer 设置一个 16ms 的定时器大约 60 帧/秒每次超时触发 tick() 函数tick 里做三件事更新所有对象状态移动、AI 决策、子弹飞行、检测碰撞、调用 update() 请求重绘。这样游戏循环就建立起来了你可以把这个过程类比成电影放映机定时器负责每 1/60 秒拉一下胶片画面刷新函数负责把当前帧画到屏幕上。为什么用 QTimer 而不是开一个 std::thread 来做循环因为 Qt 的 QPainter 绘图必须在 GUI 线程执行如果你在另一个线程里疯狂调 update轻则画面闪烁重则程序崩溃。QTimer 本质上是事件循环的一部分天然就是线程安全的。不过手动设置定时器间隔时要注意的是间隔太短比如 5ms 会因为事件循环本身的开销而达不到真实帧率16ms约 60fps是性能和流畅度的平衡点。2.2 QPainter 自绘图形 vs 贴图方案很多在校生做图形项目第一反应是找素材贴图但我的建议是坦克大战这种几何形状为主的游戏直接用 QPainter 画矩形和线条就够了甚至效果更好。QPainter 的 API 上手非常快drawRect、drawPolygon、drawPixmap 这老三样几乎覆盖全部需求而且矢量绘制的画面不管怎么缩放都不会模糊。我的渲染方案是每种坦克用 drawRect 和一个表示炮管方向的 drawLine 绘制。具体参数上玩家坦克尺寸设为 40x40 像素砖块墙每个格子 20x20 像素这样地图上一格正好放两个砖块。每种坦克用不同颜色区分——玩家用黄色普通敌人用灰色重甲敌人用绿色。这样的好处不仅仅是省资源更重要的是代码和逻辑完全可控你可以轻易地通过改变属性值来调整坦克形态而不用每次打开 Photoshop 改图。自绘的缺点不是没有比如精细纹理表现力不足。但对于一个以入门实战为目的的项目清晰明了远比画面精美重要。再说了坦克大战本就强调可读性玩家第一眼就要看明白场上单位敌我分明大色块方案反而更直观。2.3 场景对象管理与绘制调度游戏中的所有可见对象我都设计为继承自一个 GameObject 类它包含了 position、speed、direction、active 这几个基础属性和 update()、render() 两个虚函数。GameScene 中用一个 QList 保存所有对象指针tick 的时候遍历调用 update收到 paintEvent 时遍历调用 render。这个设计参考了游戏引擎中常见的组件-管理器思路好处非常明显新增一种敌人时不需要改动 GameScene 任何代码只需要建一个派生类然后加入列表。我调试时曾经把敌人 AI 改到一半代码崩溃了结果排查半天发现是空指针访问——因为只把对象 new 出来了忘了添加到 scene 的场景列表里导致 render 阶段根本没画它。这类问题很典型定位方式就是在 addGameObject 里加一个调试断言确认对象指针非空且 active 为 true 再进入列表。内存管理上需要注意Qt 的对象树机制只对继承 QObject 的类生效但 GameObject 我故意设计成普通 C 类避免引入信号槽开销。这意味着删除对象时要格外小心我选择在 GameScene::clear() 里统一释放同时确保 QList 里的指针在对象销毁后立即移除防止悬空指针。这部分属于 Qt 和纯 C 混合编程一定要想清楚的边界问题。3. 坦克核心玩法实现3.1 坦克类设计与移动控制坦克类我采用三层继承结构基类 Tank 封装公共属性和行为PlayerTank 处理键盘输入EnemyTank 则挂载 AI 控制器。Tank 中最重要的方法是 move()它根据当前方向和速度计算新坐标但在真正修改坐标之前必须做碰撞检测。void Tank::move() { int dx 0, dy 0; switch (m_direction) { case Direction::Up: dy -m_speed; break; case Direction::Down: dy m_speed; break; case Direction::Left: dx -m_speed; break; case Direction::Right: dx m_speed; break; } int newX m_x dx; int newY m_y dy; // 边界限制 newX qBound(0, newX, sceneWidth() - m_width); newY qBound(0, newY, sceneHeight() - m_height); // 碰撞检测通过才移动 if (!collidesWithMap(newX, newY)) { m_x newX; m_y newY; } }这里有两个关键点。第一是先检测后移动的时序问题如果你先改坐标再做碰撞检测物体就可能已经嵌入墙体然后又要回退一个像素或者做位置修正逻辑复杂且容易闪跳。第二是 qBound 边界钳制的作用相当于把坦克限制在场景矩形内防止它跑到窗口外面再也画不出来——这是我第一版漏掉的功能结果玩家按住方向键没多久坦克就消失了一半相当尴尬。3.2 碰撞检测的矩形相交判定坦克大战里的碰撞检测并不需要像素级精度用轴对齐包围盒AABB就足够了也就是判断两个矩形的边界是否有重叠。Qt 已经封装了 QRect::intersects() 函数但理解原理仍然有用因为以后做更复杂的游戏时你可能需要自己实现四叉树加速或者圆形检测。bool Tank::collidesWithMap(int x, int y) { QRect tankRect(x, y, m_width, m_height); const auto walls m_scene-walls(); for (const Wall* wall : walls) { if (tankRect.intersects(wall-rect())) { return true; } } return false; }这个实现有个性能隐患每帧每位坦克都要遍历整张地图的所有墙体。我的地图大概有 200 个墙体一帧最多 5 辆坦克也就是 1000 次矩形相交判断。60 帧每秒就是 6 万次听起来多但因为矩形相交判断极其轻量现代 CPU 完全没压力。真正需要忧性能的场景是成千上万个物体互相对撞那是商业游戏引擎的范畴了目前这个方案足够。如果你追求更高效的碰撞方案可以用空间格子法把地图划分成 40x40 的格子每个格子只记录当前存在的物体。检测时只需要检查坦克所在格子周边九个格子里的物体复杂度从 O(n) 降到 O(1)。这个扩展我留在了代码注释里有兴趣的可以继续加。3.3 子弹的发射、飞行与生命周期坦克的位置更新逻辑已经理清子弹的逻辑就更简单了——速度比坦克快不少方向固定飞出边界或者撞到障碍物就销毁。这里唯一需要实际处理的是对象在新旧两帧之间的穿墙问题也就是子弹速度极快时可能一帧跨越了整块墙体导致永远检测不到碰撞。解决这个问题有三种思路第一种是最简单的限制子弹每帧位移不超过最小障碍物尺寸比如我场景里最小的砖块是 20x20那么子弹速度就不能超过 20 像素/帧对应 60fps 就是 1200 像素/秒实战手感已经很快了。第二种是连续扫描检测法swept AABB把子弹的运动轨迹看作一条线段与墙体矩形做线段相交检测精度高但计算复杂。第三种是降低帧间隔同时限制速度上限。对于这个项目第一种方法就够了presentation 和性能都能兼顾。子弹的生命周期管理是这个项目最需要小心的部分。子弹击中墙体或者坦克后不能立刻 delete 自己因为它还可能处于 QList 的遍历循环中删除当前迭代项会导致迭代器失效。我的做法是把要删除的子弹放入 pendingRemove 列表等本轮 update 结束后统一清理。这个模式在游戏开发里叫 deferred deletion延迟删除能避免大量隐晦的崩溃问题。4. 敌人 AI 与战斗手感调优4.1 敌人行为的状态机设计如果你不做 AI那么坦克大战就只是坦克移动模拟器游戏性为零。敌人的行为虽然不用很聪明但至少要有追着你打的压迫感。我没有用复杂的寻路算法而是设计了一个简单状态机巡逻、追踪、攻击。void EnemyTank::updateAI() { switch (m_state) { case AIState::Patrol: // 每 40 帧随机调整方向 if (m_stateTimer % 40 0) { setDirection(static_castDirection(QRandomGenerator::global()-bounded(4))); } break; case AIState::Chase: // 朝玩家所在方向移动 chasePlayer(); break; case AIState::Attack: // 瞄准玩家方向开火 aimAndFire(); break; } }状态切换条件也很直观每帧计算敌人与玩家的距离距离小于 300 像素时切换到追击小于 180 像素且角度对得上时切换到攻击并开火。巡逻状态则是随机换方向瞎逛模拟战场上敌人毫无规律地乱走。说实话这种 AI 并不聪明玩家完全可以卡墙角反复吊打。但坦克大战的核心乐趣本来就包括用技巧戏耍 AI所以这个愚蠢程度反而变成了设计优势。如果你想让敌人聪明些可以给 Chase 状态加一个横向预判根据玩家当前速度提前计算出其未来两秒的位置然后朝那个点移动。实现不复杂但游戏难度会直接拉满新手可能撑不过第一关。4.2 多敌人并发管理与波次生成游戏最开始我没有做波次系统而是开局就把所有敌人放出来结果画面乱成一团玩家三秒钟就挂。后来参考原版的设计改为场上最多同时存在 3 个敌人总共 20 个敌人分波次从三个出生点随机刷新。这个改动对整个游戏的节奏影响极大——玩家有喘息空间能逐个击破而不是被围攻到死。生成逻辑放在 GameScene 里每一帧检查场上敌人数量少于 3 个小队队列里还有剩余就生成新的。出生点有三个左上、右上、右下老家左下不生成敌人。出生时加一个 1 秒的无敌保护期防止敌人刚出来就被子弹堵门口打死这既公平也保留了操作空间。多敌人的管理还引发了一个经典问题所有敌人共用一个 AI 控制器类但每个实例持有自己的状态。因为我没有用共享静态变量而是把 AI 状态作为 EnemyTank 的成员所以互相之间完全独立。写扩展时注意这一点否则容易出现一个敌人被攻击全场敌人全跑来追你的离奇行为。4.3 音效与得分反馈的手感优化游戏手感很大程度上来自即时反馈。我加入了开火、击中墙体、击毁坦克、基地爆炸四种音效以及击毁敌人后的分数飘字效果。音效用 QSoundEffect 加载 WAV 文件实现它还支持调整音量大小和播放次数。真正制作音效素材时我没有自己录制而是从开放音效库找的你也可以用程序生成简单的音效——Qt 自带 QAudioFormat理论上可以自绘波形但工作量太大不推荐新手去做。得分反馈用了一个小技巧击毁坦克时在爆炸位置创建一个 FloatingScore 对象它不参与碰撞只是向上飘动并在 0.8 秒内淡出。这类瞬时 UI 元素和子弹一样用延迟删除来管理生命周期。这种细节玩家不会说出来但恰恰是它们让游戏显得不廉价。我自己测试时对比过有无得分飘字的版本差距很明确——没有飘字时击杀后总觉得少了点什么加上后操作反馈完整了割草感一下就来了。5. 界面打磨与工程发布5.1 游戏状态管理菜单、战斗、暂停、结算游戏不能只有战斗场景至少要有开始菜单、暂停、Game Over 三个界面。我用 QStackedWidget 管理三个页面菜单页是一个 QWidget 带开始游戏按钮战斗页是包含 GameScene 的容器结算页显示分数并提供再来一局按钮。QStackedWidget 是 Qt 里非常适合做多页面切换的组件它把多个页面叠在一起通过 setCurrentIndex 切换当前显示的页面。程序启动时显示菜单页点击开始后切换到战斗页并启动定时器战斗中按 Esc 弹出暂停遮罩一个半透明 QWidget 覆盖在坦克场景上方再按一次恢复玩家生命归零时跳转到结算页。游戏状态机的设计经验是不要让状态布尔变量散落在各个类里而是单独整一个 GameState 枚举Menu, Playing, Paused, GameOverGameScene 里的 tick 和 paintEvent 首先检查当前状态非 Playing 状态直接跳过更新逻辑。这样状态管理一目了然新加状态时只改枚举和判断处即可。5.2 用 qrc 管理资源文件音效、字体、贴图等资源文件我全部放进 Qt 的资源系统.qrc 文件里而不是以磁盘路径引用。资源文件会被编译进可执行文件好处是发布时不用额外带一堆素材目录而且资源路径跨平台不会出现盘符分隔符不一致的坑。RCC qresource prefix/ filesound/fire.wav/file filesound/explode.wav/file filesound/bonus.wav/file /qresource /RCC在代码里就可以这样访问QSoundEffect::setSource(QUrl(qrc:/sound/fire.wav))。注意 Materia资源系统的一个坑如果你新增了文件到 qrc必须在 Qt Creator 里重新构建不一定要重新编译所有文件但至少要让 qrc 的依赖重扫一遍否则新资源不会被打进二进制里。我在第一次打包发布时吃了这个亏——本地运行一切正常拿给朋友 exe 文件却发现按钮点击没反应排查半天才发觉是资源没编进去。5.3 打包发布与 windeployqt 使用开发调试阶段一切正常真到发布阶段才发现 Qt 程序的打包是个技术活。Qt 程序是动态链接 Qt 运行库的直接把 exe 拷到别的电脑上运行最常见的报错就是热词里提到的windows no qt platform plugin could be initialized。这个错误是说系统找不到 platforms 目录下的 qwindows.dll。解决办法是使用 Qt 自带的部署工具 windeployqtcd 你的构建目录 # 确保里面有生成的坦克大战.exe C:\Qt\5.15.2\mingw81_64\bin\windeployqt.exe 坦克大战.exe注意两点第一windeployqt 要在目标平台对应的 Qt 工具链目录下运行用错了版本会复制错误的 DLL。第二一定要对 release 版本的 exe 执行debug 版带调试符号打出来的包大而且运行时还得额外配调试库。执行完毕后目录下会多出 platforms、styles、imageformats 等子目录以及一堆 Qt5*.dll整个文件夹拷给别的 Windows 机器就能直接跑。提示如果你使用了 Qt Multimedia 模块windeployqt 不会自动帮你复制所有多媒体解码器有时需要确保目标机器装了对应解码组件。保险的做法是把自己开发机上 Qt 安装目录里的音效格式插件也都拷过去但这部分我一个都没漏正常发布是没问题的。5.4 源码工程结构与后续扩展方向最终整理好的工程结构如下TankBattle/ ├── TankBattle.pro # qmake 工程文件 ├── src/ │ ├── main.cpp │ ├── GameScene.h/cpp │ ├── Tank.h/cpp │ ├── PlayerTank.h/cpp │ ├── EnemyTank.h/cpp │ ├── Bullet.h/cpp │ ├── GameMap.h/cpp │ └── ... ├── res/ │ ├── sound/ │ └── qrc 资源文件 └── README.md源码完全开源你可以自由修改并重新编译。如果觉得两星期一个项目太简单我建议的扩展方向有双人同屏模式一个用 WASD 控制一个用方向键、道具系统加血、无敌、双倍子弹、地形破坏效果子弹打碎砖墙的碎块粒子模拟、关卡编辑器。我自己接下来大概率会往道具系统方向做因为坦克大战最让人上头的就是吃满三个星星变成金色坦克那种成长感给玩家短期目标会让游戏的反馈循环更完整。6. 常见问题排查与踩坑记录6.1 Qt 环境与编译常见报错这个项目反复编译部署踩过的坑罗列一下大部分都是从 Qt 新手群和论坛里看到的高频问题报错信息原因解决方案undefined reference to vtable for Tank头文件含 Q_OBJECT 但没重新运行 qmake/moc在 Qt Creator 中执行构建 清理并重新构建no qt platform plugin could be initialized缺少 platforms/qwindows.dll用 windeployqt 部署或检查程序目录结构cannot find -lGLLinux 下缺少 OpenGL 库安装 libgl1-mesa-devUbuntu 上执行 apt installmoc: file not found忘记在 .pro 里添加头文件确保 .pro 的 HEADERS 变量包含所有带 Q_OBJECT 的头中文乱码源码文件编码与编译器不匹配统一使用 UTF-8 编码或用 QStringLiteral 包裹中文字符串最后一条重点说明Qt 5.15 用 UTF-8 编码编译的话直接在源码里写中文字符串基本没问题但 Qt 6 已经强制要求 QStringLiteral 或 u8 前缀否则在 MSVC 下会因为窄字符编码不一致出现乱码。这个和坦克大战本身关系不大但做其他项目一定会碰到。6.2 游戏运行的性能问题与绘图闪烁修复我测试过程中遇到最经典的渲染问题就是闪烁。闪烁的根源在于 QPainter 绘图是先清屏再画内容如果绘制代码效率不高清屏后的短暂间隙就会显示白色背景视觉上就像画面在闪。解决方法有两个层面。第一是在 QWidget 构造函数里设置setAttribute(Qt::WA_OpaquePaintEvent, true)告诉 Qt 不需要先清除背景因为我会在每一帧主动绘制整个场景的所有内容。第二是在 paintEvent 里把所有绘图操作放到一个 QPainter 对象中连续执行尽量减少 paint 函数的调用次数。实测这两步做完画面闪烁基本消失CPU 占用也从 30% 下降到 12% 左右。另一个优化点是避免在 paintEvent 里动态 new QPen、QBrush 这类临时对象。Qt 的绘图表象类构造有一定开销每一帧都 new 不仅拖慢速度还可能导致内存碎片。我把常用的画笔和画刷作为 GameScene 的成员缓存好了构造时初始化一次后续反复复用。6.3 布局与坐标的边界细节坦克大战的坐标系看似简单但有个特别容易被忽略的细节屏幕原点在左上角X 轴向右Y 轴向下。数学坐标系里的向上会导致 Y 值减小而很多新手会习惯性地写成m_y - m_speed表示向上这在逻辑上是对的但如果你拿场景高度去校验边界就会写反判断条件。我建议处理方向时用一个 direction 枚举加上统一的 move() 方法避免到处分散地写加加减减的语句减少该类低级错误。地图数据我用了二维数组表示0 代表空地1 代表砖块2 代表钢块3 代表河流。初始化地图时根据数组数字生成对应的 Wall 对象同时记录每块墙的矩形位置以便碰撞检测。如果你要自定义地图只需改这个数组不用改任何代码——这是把数据与逻辑分离的典型做法。6.4 调试技巧与日志记录方式项目不复杂时直接在关键位置加 qDebug() 输出没问题。但当敌人数量和子弹数量多起来之后逐行加打印非常影响实时性而且输出量一多肉眼根本跟不上。我的调试心得是第一给关键对象加一个 id 属性打印日志时带上 id 和对象类型例如qDebug() Bullet # m_id collided at pos;这样能从大量日志中快速定位到具体某个对象的行为轨迹。第二调试碰撞逻辑时在 GameScene 里增加一个 debug 模式快捷键按空格键在控制台打印出当前场景所有激活对象的坐标和方向而不是疯狂刷 qDebug。第三用 Q_ASSERT 检查关键前置条件比如坦克坐标不能为负数、子弹速度必须大于零、敌人 AI 状态必须合法。Release 构建时 Q_ASSERT 会自动剥离不影响最终性能。写在最后做完这个项目最明显的感受是C 语法的书翻三遍不如亲手写一个 2000 行的 Qt 程序来得踏实。很多知识点对象生命周期、虚函数、const 引用、STL 容器单独拿出来都会但组合在一起才能真正理解为什么这样设计。如果你准备照着这个项目来学习我的建议是把代码读完一遍后合上源码自己从零开始重写。重写时你会发现很多细节第一遍根本没看懂比如子弹延迟删除的时机、碰撞检测时为什么要传 m_x dx 而不是等修改完再检测、Q_OBJECT 到底起了什么作用。这些代码读懂了但写不出来的知识点才是真正需要花时间消化的部分。重写过程中遇到问题再回头看源码对照比自己瞪着屏幕干想要高效得多。最后分享一个个人习惯每完成一个功能点就提交一次 git提交信息写清楚做了什么、为什么这样做。这不只是版本管理的需要更是在逼自己理清设计思路。项目源码仓库里有完整的提交记录从 init 到最终可玩版本一共 17 次提交你可以看到游戏是怎么一步步从空白窗口长成完整作品的——这比直接看最终代码更能让人理解开发过程中真实决策的演变。本文还有配套的精品资源点击获取
返回列表