📄 一句话概括
本章把前面十五年的零散知识装成了一个能跑的窗口游戏: 一张知识地图 + 十个必记结论 + 三张判断表 + 两关自测。 更重要的是 —— 这一节也是全书的收官:《零基础学C++》16 章,到此读完。
📝 本节和前面几节的关系
这是全书最后一节。它不像别的小结只回首一章,它要回首整整十六本账:
- 14.3 / 15.7 的小结只收一章;本节要收"从 C++ 概述到游戏完成"的全过程;
- 本节的"三张判断表",是把本章最容易混的三个选择(窗口还是控制台、
GDI 还是 GDI+、数组还是 vector)固化成速查;
- "两关自测" 是给这一章做的结业考(比前几章的小结更硬 —— 因为这一章本身就是综合题)。
一句话:前面是"学",这一节是"结账"。
一、本章知识地图
第十六章 坦克动荡游戏
│
┌─────────────┬───────────────┼───────────────┬─────────────┐
│ │ │ │ │
【背景与设计】 【工程与框架】 【绘图】 【游戏功能】 【整合】
│ │ │ │ │
16.1 开发背景 16.3 创建项目 16.5 GDI+ 16.6 详细设计 16.7 实现
16.2 系统设计 16.4 游戏框架 │ │ │
│ │ ├ 引入/初始化 ├ 可移动对象 ├ 公有接口
├ 功能结构 ├ 主窗口 ├ 画帧数 ├ 坦克/子弹 ├ 私有帮手
├ 业务流程 ├ 游戏循环 ├ 图片资源 ├ 地图/轨迹 ├ update
└ 功能→类 ├ 鼠标响应 └ 菜单背景 ├ 碰撞/反弹 ├ render
└ 接进窗口 ├ 自动寻路 └ 接进主窗口
├ 菜单
└ 单人/双人
全章 8 节,一条主线: 先想清楚(16.1~16.2)→ 把架子搭起来(16.3~16.5)→ 往里填内容(16.6)→ 拧成一台机器(16.7)。
二、必须记住的十个结论
- 游戏和普通程序的分水岭是"游戏主循环" ——
while (运行中) { 输入 → 更新 → 绘制 },每秒几十次,永不结束。 - 游戏循环必须用
PeekMessage,不能用GetMessage—— 后者没消息会睡着,游戏逻辑就不走了。 - "请求重画"≠"真的重画" —— 用
InvalidateRect请求,系统发WM_PAINT给你画;合法的hdc只在那里拿得到。 - GDI+ 用前
GdiplusStartup、用后GdiplusShutdown—— 成对出现,位置分别在程序的开头和结尾。 - 屏幕坐标 y 轴向下 ——
0°朝右,90°朝下;角度制与弧度制只在cos/sin前转换。 - 移动要"分轴" —— 横向被挡就只走纵向,这样撞墙能贴墙滑行而不是卡死。
- 子弹反弹两个公式 —— 撞竖墙
a' = 180° − a;撞横墙a' = −a—— 两个都要写。 - 碰撞检测比平方,不开方 ——
d² ≤ r²就够了,每帧算几十次,省下来很实在。 - "固定用数组,可变用容器" —— 坦克 2 台用
CTank[2];子弹数量会变 →std::vector。 - 状态机让"同一个窗口做不同的事" —— 一个
state+switch,菜单 / 游戏中 / 结算三张画面共用一个render。
三、三张判断表
表一:这个程序用控制台还是窗口?
| 你的程序要…… | 用 | 为什么 |
|---|---|---|
| 算数、读文件、练语法 | 控制台(Dev-C++ 也行) | 简单、专注逻辑 |
| 显示图形、收鼠标、做游戏/软件界面 | 窗口(Visual Studio) | 需要 Windows API + GDI+ |
表二:用 GDI 还是 GDI+?
| 你的需求 | 用 | 说明 |
|---|---|---|
| 画几条直线、不讲究好看 | GDI | 老函数(TextOut/Rectangle) |
| 要抗锯齿、渐变、半透明、读 PNG | GDI+ | 本章的选择 |
表三:这个数据用数组还是 vector?
| 数据的特点 | 用 |
|---|---|
| 数量固定(两台坦克、一张地图的行) | 数组 / std::string |
| 数量会变(子弹越打越多、越清越少) | std::vector |
四、两关自测
第一关:给游戏加一个"计时器"
要求:在主画面上显示本局已经进行了多少秒,并做到:
- 只在
ST_PLAYING状态下计时(菜单里不计、结算后停); - 显示在右上角(左上角已经被帧数占了);
- 用
GetTickCount()算时间差,不要用frame除以 60 硬凑(帧率不是恒定的)。
参考思路(不唯一):
// CGame 里加两个成员
DWORD startTick; // 本局开始的时刻
int seconds; // 已经过去的秒数
// startGame 里
startTick = GetTickCount();
seconds = 0;
// update 里(在 state 检查之后)
seconds = (GetTickCount() - startTick) / 1000; // 毫秒 → 秒
// drawWorld 里
wchar_t buf[64];
swprintf_s(buf, L"时间: %d 秒", seconds);
Font f(&ff, 16, FontStyleRegular, UnitPixel);
g.DrawString(buf, -1, &f, PointF(640, 10), &yellow); // 右上角
验收:菜单里数字不动;开局后每秒 +1;结算后停住;重开一局归零。
第二关:十五个坑,逐个判断
| # | 说法 | 对不对 | 为什么 |
|---|---|---|---|
| 1 | 主循环用 GetMessage 就够了 |
❌ | 没消息会阻塞,游戏逻辑不跑 |
| 2 | 在循环里直接 render(hdc) |
❌ | 拿不到合法 hdc,要走 WM_PAINT |
| 3 | InvalidateRect 之后立刻会画 |
❌ | 只是"请求",系统决定时机 |
| 4 | 忘记 GdiplusShutdown 无所谓 |
❌ | 资源不回收 |
| 5 | 角度可以用弧度和角度混着写 | ❌ | 本章统一角度制 |
| 6 | 坦克撞墙时整步取消移动 | ❌ | 会卡死;要分轴移动 |
| 7 | 子弹反弹统一写 angle = -angle |
❌ | 只对横墙;竖墙是 180 − angle |
| 8 | 碰撞检测必须开方 | ❌ | 比平方就够 |
| 9 | Graphics 旋转后要 Restore |
✅ | 否则后画的全歪 |
| 10 | 子弹用固定数组更方便 | ❌ | 数量会变,用 vector |
| 11 | 菜单必须用按钮控件 | ❌ | 矩形 + 命中测试即可 |
| 12 | 单人/双人要写两套移动逻辑 | ❌ | 一个 isComputer 统一 |
| 13 | update 里先判胜负再处理子弹 |
❌ | 顺序反了,会漏判 |
| 14 | 图片加载失败会报错 | ❌ | 安静失败,要查 GetLastStatus() |
| 15 | 每帧要"擦掉"上一帧的子弹 | ❌ | 整幅重画,不需要局部擦除 |
得分:答对 15 个 → 这一章真的过了;答对 10~14 个 → 回去翻对应小节;10 个以下 → 16.4 ~ 16.7 重读一遍。
五、全书收官:这十六本账
回顾一下,从"什么是 C++"到"写完一个游戏",你都学了什么:
| 阶段 | 章 | 一句话 |
|---|---|---|
| 入门 | 第 1~2 章 | 认识 C++、认识环境、会读会写最基本的程序 |
| 语法基础 | 第 3~6 章 | 运算符、分支、循环、函数 —— 程序的三块积木 |
| 数据 | 第 7~8 章 | 数组、指针、引用、结构体 —— 数据怎么摆 |
| 面向对象 | 第 9~11 章 | 类、继承、多态 —— 代码怎么组织 |
| 泛型与标准库 | 第 12~13 章 | 模板、STL —— 不再重复造轮子 |
| 进阶主题 | 第 14~15 章 | RTTI、异常、文件 —— 出错不崩、数据能存 |
| 综合实战 | 第 16 章 | 把以上全部用一遍,做出一个完整的游戏 |
这本教材到此,全部完结。 🎉
三个"带得走的"东西(比语法更值钱):
- "分而治之"的习惯 —— 大问题拆成小问题,小问题拆成函数和类;
- "先设计后动手"的习惯 —— 16.2 那一节不是走过场;
- "立刻检查"的习惯 —— 打开文件要检查、加载图片要检查、分配内存要检查。
接下来可以往哪儿走(几个方向,任选):
| 方向 | 学什么 | 为什么 |
|---|---|---|
| 图形界面 | Qt | 跨平台、语法优雅,比 Win32 舒服得多 |
| 游戏开发 | Unreal / Godot | 本章的游戏循环和对象体系是基础 |
| 算法功底 | 数据结构与算法 | 本章的 BFS 只是一块敲门砖 |
| 现代 C++ | C++11/14/17/20 特性 | auto、智能指针、lambda、std::filesystem |
| 继续做项目 | 把本章的游戏扩展 | 加音效、加地图编辑器、加联机 —— 一个项目可以吃很久 |
六、常见误区(本章汇总)
- "总结就是再看一遍目录" —— 不是。要把"结论"和"判断表"记下来,下次遇到同类问题直接查表。
- "游戏跑起来就行了,不用管结构" —— 不行。加一个功能就乱了 —— 16.2 的设计和 16.7 的分工,都是为了让"加功能"变容易。
- "项目做完就扔了" —— 太可惜。把它当模板:加个计分板、加个道具、换张地图 —— 每加一样,都是真本事在长。
- "书读完了就学会了" —— 只算"见过"。真正的学会,是自己从零再写一遍 —— 哪怕写得慢、写得丑。
自测三问(点开对答案)
1. **本章八节的主线是什么?** —— **先想清楚(16.1~16.2 背景与设计)→ 搭架子(16.3~16.5 项目 / 框架 / 绘图)→ 填内容(16.6 功能)→ 拧成机器(16.7 整合)。** 2. **这个游戏里,三个"最值钱"的设计决定是什么?** —— **① 用 `CMovable` 抽象出坦克和子弹的共同部分**(**继承 + 多态省一半代码**);**② 用 `isComputer` 统一单人/双人**(**一个标志代替两套逻辑**);**③ 用 `state` 状态机统一三种画面**(**同一个窗口做不同的事**)。 3. **如果让你重写一遍,最想先改哪里?** —— **开放题**。**常见答案:把"全局 `g_map`"改成"CGame 传参"(耦合更低)、把坦克/子弹的碰撞半径做成可配置、把地图格式加个头信息。** **能说出"哪里不够好",说明你真的读懂了这一章。**- ☑ 全书完结 —— 《零基础学C++》16 章已全部读完