查看源码下载文档
CHAPTER / 17

阅读实验、调试路线与制作自己的 RTS

1. 学习目标:能解释、能预测、能验证

源码阅读最容易产生的错觉是“所有文件名都眼熟,因此已经理解”。更有用的标准是:给出一个变化,你能预测哪些字段、函数和系统受影响,并指出需要什么证据证明预测。

以下实验分为两类。前五个仅靠仓库和文本工具即可完成;后三个需要你后续自行建立可运行版本,本文只给实验设计,没有声称已经执行。它们不依赖你先把全部历史工程修好。

2. 实验一:给一个坦克制作数据身份证

任务:选择 2TNK,列出它的枚举、静态原型、类型池建立点、INI 读取点、主要运行期类。

先查 UDATA.CPP 的原型,再查 UnitTypeClass::Init_HeapRead_INI,最后沿继承找到 TechnoTypeClass::Read_INI。不要把静态原型中的每个布尔参数凭顺序猜成游戏属性;对照构造声明核验。原型类型池初始化共同数据读取

合格产出:一张表中区分“代码固定、INI 可覆盖、实例独有”三类字段。若缺少实际 RULES.INI,武器、价格和生命的最终值应写“待资源确认”,不能根据记忆填入并称为源码结果。

检验题:只添加 [MYTANK],为什么不一定增加一个能生产的新兵种?答案应涉及类型注册、名称解析、枚举边界、资源以及生产资格,而不只是“因为没有图片”。

3. 实验二:手算规则转换,不混淆单位

任务:用第05章教学输入,解释 Range=5Speed=50Strength=420 分别变成什么。

距离经 Get_Lepton 以格为输入,转换成内部坐标量;车辆速度经过 Techno 的缩放;生命是普通整数,但后续仍有类型、难度和实例状态影响。距离转换速度缩放

预期结果:按一格256的坐标量,Range 得1280;Speed 50缩放为128。这两项都不是墙上时钟计时的“米/秒”。数值进入不同消费者时还会受到地形、任务、计时和取整影响。

扩展题:分别比较 Speed=99100101-1 的裁剪与取整;再比较武器速度读取和车辆速度读取是否使用同一函数。目的不是证明“写负数能作弊”,而是学会追踪每个字段自己的转换契约。

4. 实验三:画出一条有障碍的寻路过程

任务:在纸上画一块小网格,加入一个 L 形障碍,标出起点和目标。对照第04章,跟踪直线尝试、碰障后的两侧边缘搜索、路径选择和平滑。

合格产出:标明每一步检查的是静态地形、移动类别、区域还是动态占用。说明为什么到达相同目标的不同单位可能得到不同结果,以及为什么已有路径仍会因另一辆车挡路而失效。

不能直接画一张标准 A* 的 open/closed 表然后称为本项目算法。先检查 FINDPATH.CPP 的实际状态和循环,再使用自己的算法术语。寻路实现文件

扩展题:你会怎样构造一个让局部绕障产生不理想结果的地图?预测应明确是无法到达、路径过长还是容易拥堵,三种现象不能混称“寻路错误”。

5. 实验四:从“点了却不动”定位问题层级

按顺序寻找以下源码入口:FootClass::Active_Click_WithTechnoClass::Player_Assign_MissionQueue_MissionEventClass::Execute、任务分派、寻路和移动提交。

可使用这些只读检索,路径大小写按仓库保留:

bash
rg -n 'Player_Assign_Mission|Queue_Mission' CODE/TECHNO.CPP CODE/QUEUE.CPP
rg -n 'case MEGAMISSION|Assign_Destination' CODE/EVENT.CPP
rg -n 'MissionClass::AI|Commence|Assign_Mission' CODE/MISSION.CPP

合格产出:给三个不同故障各安排一个最早可辨别的观察点:没有入队、事件执行时对象失效、路径被障碍阻断。避免在还没确认任务写入之前就改寻路代码。入队函数执行时检查

6. 实验五:核查一条注释,找出真正契约

选一个看起来含义很明确的函数,例如生产扣款、伤害衰减、连接包顺序或智能指针。分开抄下“注释承诺”和“函数体实现”,再查调用者是否依赖该承诺。

示范是 FactoryClass::AI:阶段先推进,钱不够时退回;余额归零与暂停在完成条件里处理。真正契约要从分支中读出来,而不是只把它概括成“更新生产”。阶段和资金代码

合格产出:对疑点写出条件、字面结果和待验证部分。不要把所有不符合现代习惯的代码立即叫作 bug,也不要因为注释完整就忽略代码相反的证据。第A4附录提供更多可练习对象。

7. 实验六:建立最小可运行观察场景(待环境复原)

在具备合法游戏资源和可调试构建后,先做只有一名玩家、几辆普通坦克、一个障碍和一个靶建筑的场景。固定规则、初始随机状态和命令序列。

建议记录帧号、对象稳定标识、任务、坐标、NavCom、TarCom、开火计时、生命和关键创建/销毁事件。一次只改一个参数,如炮弹伤害或射程,并比较轨迹。不要同时更换编译器、修改寻路、调整平衡和提高帧率,否则差异无法归因。

通过标准:不仅最后胜负相同,关键状态轨迹也能解释。若首个不同点在对象创建顺序,后面伤害与随机结果可能只是其连锁后果。

8. 实验七:确定性与存档恢复(待环境复原)

对同一初始状态和命令流运行两次,记录同步相关状态。先确认同一构建能复现,再比较迁移构建。若不同,定位第一处状态分歧,而不是只对最终截图。

存档试验则在某帧保存,继续运行一段;另一路加载后输入相同后续命令,比较对象关系、队伍、工厂、任务和随机序列。类型、虚表、池槽与指针编码均可能影响恢复,详见第14章

特别注意:新增日志应只读状态。这个项目中把随机对象转换成整数会取下一个随机值;为了打印而触发这种转换,可能改变你要观察的程序。随机对象整数转换

通过标准:明确比较了什么状态、忽略了哪些本地表现数据、从哪个帧开始一致,不能仅以“没崩溃”宣布兼容。

9. 实验八:替换一个平台模块(待环境复原)

如果目的是学习移植,选一个边界明确的模块,如文件读取、像素输出或定时器。先保留游戏层接口,再用现代实现替换底层,建立可比较的输入输出。

像素输出可比较索引色缓冲、调色板与最终截图;定时器可比较不同游戏速度下的逻辑推进和等待行为;音频可比较采样格式、播放结束通知和资源生命周期。它们的通过条件不同,不能以“窗口弹出”代替渲染正确,或以“能听到声音”代替播放队列正确。

修改整数宽度、结构布局和对象遍历顺序属于更深的兼容性工作。先证明旧行为,再决定哪些行为有意改变。迁移路线和构建缺口详见第15章

10. 今天制作一个小 RTS,哪些思想值得留下

设计问题 可以借鉴的思想 今天可明确化的部分
大量同类单位 类型数据与实例分开 类型 schema、稳定 ID、引用检查
多种操作来源 将输入表达为语义命令 命令版本、验证、可记录执行顺序
持续任务 显式状态与定时推进 状态图、调试可视化、可测的转移条件
世界空间 占用、地形、移动能力分开 清晰的空间查询契约与动态障碍策略
内容制作 规则与场景覆盖 覆盖顺序、单位、错误提示和编辑器约束
运行一致性 固定规则、顺序与随机序列 确定性回归、版本化数据和状态摘要
对象生命周期 入场、离场、销毁显式处理 带代次的句柄、所有权和订阅清理
平台迁移 封装文件、显示、计时、音频 平台边界与可验证后端

这些是工程建议,不是声称原项目已经实现所有现代保障。也不必把所有历史结构原封不动搬进新游戏。深继承、全局变量、固定容量池、汇编优化都与当时约束有关;是否适合今天,要看项目规模、目标平台和兼容性要求。

11. 建议的自制 RTS 里程碑

  1. 可观察世界:网格、单位、选中与移动,能显示坐标、任务和路径。
  2. 完整战斗闭环:目标、射程、冷却、弹体、伤害、销毁及引用清理。
  3. 经济闭环:资源、采集、生产、扣款、交付失败处理。
  4. 内容与局部自主行为:类型配置、任务状态机、少量关卡触发器。
  5. 可复现性:命令记录、稳定更新顺序、保存恢复;若要联机,再建立同步层。
  6. 表现与工具完善:动画、声音、侧栏、资源制作和性能分析。

每一步应让前一步继续可观察、可解释。没有必要一开始复刻全部兵种、剧情影片或旧联网服务;但若目的是兼容原版,则必须把兼容范围明确写成独立目标,不能把自制近似玩法称为已经移植原引擎。

12. 本资料的验证产物

提供的 index_source.py 会重新统计跟踪文件、行数和 SHA-256;audit_guide.py 会核对源文件链接、提交和行号范围。它们帮助复核这套分析与哪个源码快照对应。

已经完成的是源码获取、主要子系统静态研读、文档交叉核验和结构检查。这里设计的游戏运行、网络联机、存档往返与移植实验尚未执行;相关原因和剩余问题见覆盖附录

红色警戒初代 · 源码剖析18 章正文 / 5 份附录
输入关键词,检索 24 份完整文档
全文检索 · 点击结果直达对应小节