This commit is contained in:
meishibiezb
2026-06-04 21:37:53 +08:00
parent b0d2a0e2e7
commit 29a3f77908
63 changed files with 4068 additions and 1 deletions

415
HOWTO_REGENERATE_DOCS.md Normal file
View File

@@ -0,0 +1,415 @@
# 文档生成操作手册
## 这篇文章是干什么的
当你需要更新这个项目的文档,或者想把同一套方法套到另一个 UE 项目时,看这个就够了。
本文记录:
1. **踩过的坑** — 初版被骂的 10 项问题,别再犯
2. **正确的文档模板** — 每个单位应该长什么样
3. **完整操作步骤** — 从零到 56 个文档是怎么生成的
4. **工具和命令** — 用了什么、怎么用
---
## 一、血泪教训:初版的 10 项问题
第一次跑完「项目文档化计划」后,被用户逐条指出以下问题,全部在修订版中修正:
| # | 问题 | 初版做法 | 正确做法 |
|---|------|---------|---------|
| 1 | 多个单位合并到一个文件 | `ItemDataStructures.md` 里塞了 3 个 struct | 每个结构体/类/接口/枚举独立一个 .md |
| 2 | "设计用意"和"职责范围"合并 | 一个 `## 设计用意与职责范围` 章节糊在一起 | 拆成两个独立章节 |
| 3 | "职责范围"用二分法 | 写"职责xxx / 不负责yyy" | 自然语言描述工作范围 |
| 4 | 多余的模块 | 文档里出现"变量"、"关键成员"表格 | 不写实现细节,对外接口从调用者视角描述 |
| 5 | 使用方法不引用真代码 | 写"可以这样用"但没有出处 | 每条引用真实文件路径和行号 |
| 6 | 用例编造示例代码 | 写伪代码 `// 继承 AMyActor...` | 只列出项目中实际使用该单位的文件 |
| 7 | `_relationships.md` 太花哨 | ASCII 艺术图、设计模式分析 | 简单的文件级依赖表格 + 文本箭头 |
| 8 | 执行顺序混乱 | 边写接口边写依赖 | 严格:孤立文档 → 关系文档 → 接口+用例 |
| 9 | README.md 没更新 | 还是旧的待办列表 | 更新为完整的文档索引 |
| 10 | "项目内依赖"列名不对 | 用了"说明"列 | 改为"源文件"列,放文件路径 |
### 为什么这些问题很重要
- **合并文件**:后人要找 `FItemView` 的文档时,不能一眼看到文件名,还得打开文件在里面搜。单位名和文件名一一对应,是文档可发现性的底线。
- **设计用意 vs 职责范围**:前者回答"为什么存在",后者回答"管什么事"。混在一起会让维护者搞不清这个类的边界。
- **真实代码引用**:没有行号的使用说明是废纸。半年后代码改了,维护者无法验证文档是否过期。
---
## 二、正确的文档模板
### 2.1 孤立单位文档模板
每个单位class/struct/enum/BP一个 .md 文件,**严格**以下结构:
```markdown
# [单位名称]
## 基本信息
- **类型**: UCLASS / USTRUCT / UENUM / C++ class / Blueprint
- **父类**: XXX没有就写 —)
- **源文件**: path/to/file.h蓝图写 /Game/ 路径)
- **模块**: ModuleName
## 功能概述
[一段话概括这个单位做什么,什么场景用]
## 设计用意
[为什么这样设计,解决什么问题,在系统中的定位]
## 职责范围
[自然语言描述该单位承担的工作范围,它负责什么流程/数据/决策。
禁止使用"职责"/"不负责"二分格式]
## 项目内依赖
| 依赖项 | 关系 | 源文件 |
|--------|------|--------|
| XXX | 包含/引用/继承/调用 | path/to/file.h:行号 |
## 对外接口
[从外部调用者的角度描述:
- C++ 调用者:哪些 public 方法/虚函数可调用
- 蓝图调用者:哪些 UFUNCTION/BlueprintNativeEvent 可调用
- 委托:哪些委托可绑定
- 结构体:哪些 UPROPERTY 字段对外可读写
禁止单纯罗列函数签名]
## 使用方法
[引用项目中的真实代码位置说明如何使用本单位的典型流程。
每条使用方式必须给出源文件和行号出处]
## 用例
[列举项目中实际使用本单位的文件和上下文。不编造示例。
格式:文件路径:行号 — 用途说明]
```
### 2.2 关系文档模板 (`_relationships.md`)
每个模块目录下一个:
```markdown
# [模块名] 依赖关系
## 文件间引用关系
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
|--------|---------|---------|---------|
| XXX.h | #include | YYY.h | YYY |
| XXX.h | forward-declare | — | ZZZ |
## 关键依赖链
[用简单的文本箭头描述关键数据/控制流,如:
DefaultContainer.cpp → ItemFactory::CreateItemInstance → FItemInstance
]
```
### 2.3 README.md 模板
`Document/README.md`,核心是每个模块的完整单位列表表,含:单位名、文件名(链接)、类型、父类。
---
## 三、完整操作步骤
以下是在一个全新的 UE 项目上复现整个过程的操作步骤。
### 前置条件
1. **Claude Code CLI**(本文使用的 AI 编程助手)
- 安装方式见 [Claude Code 官方文档](https://docs.anthropic.com/en/docs/claude-code)
- 关键是它支持 Task 工具(可以启动后台子代理并行干活)
- 支持 MCP用于查询 UE 编辑器中的蓝图信息)
2. **UnrealClaude MCP 插件** — 安装在项目的 `Plugins/UnrealClaude/`
- 提供 `unreal_blueprint_query``unreal_asset_search` 等 MCP 工具
- 用来查询蓝图资产信息(因为在 .uasset 二进制文件上没法直接 grep
3. **ripgrep** (`rg`) — 用于在源代码中搜索单位引用
4. **bash shell** — Windows 上用 Git Bash 或 WSLmacOS/Linux 直接用
### 步骤 1确定文档化范围
先画出要文档化的所有单位清单:
```bash
# 列出所有需要文档化的 C++ 头文件
find Source/ -name "*.h" | grep -v Intermediate | grep -v Generated
find Plugins/ -name "*.h" -path "*/Public/*" | grep -v Intermediate | grep -v Generated
# 列出所有需要文档化的蓝图(通过 MCP
# 在 Claude Code 对话中使用:
# mcp__unrealclaude__unreal_blueprint_query operation=list path_filter=/Game/Blueprints/
```
对每个头文件,手动列出它包含的所有单位(一个 .h 可能包含多个 class/struct/enum。这就是你的文档化清单。
### 步骤 2分析所有源文件
这是最耗时但最关键的一步。你需要从每个源文件中提取:
- 单位的类型、父类、所属模块
- 功能概述(从注释和代码推断)
- 设计意图(为什么存在)
- `#include` 依赖关系
- 前向声明
- public 方法、UFUNCTION、delegate
**用 Claude Code 的做法**(推荐):
启动多个 `Task` 子代理并行分析:
```
对 Claude Code 说:
"用 Explore 子代理分析 D:\workspace\u\lonese\Plugins\Item\ 目录下所有 .h 和 .cpp 文件,
对每个 class/struct/enum 提取:类型、父类、功能、设计意图、依赖、公开方法。"
```
关键提示词要点:
- 让代理先 `Glob` 找到所有 .h 文件,再逐文件 `Read`
- 要求结构化输出(每个单位一个条目)
- 明确列出需要的字段:类型/父类/文件路径/模块/功能/设计意图/include依赖/前向声明/公开方法/UFUNCTION/delegate/BlueprintNativeEvent
**手动做法**(如果没有 AI 工具):
```
对每个 .h 文件:
1. 记录文件中定义的所有 UCLASS/USTRUCT/UENUM/接口
2. 记录每个单位的父类和包含的引擎头文件
3. 搜索 #include "项目内头文件" 记录依赖
4. 搜索 forward declaration
5. 记录 UPROPERTY 和 UFUNCTION 标记
6. 从类/方法注释中提取功能描述
```
结果应该整理成一张大表,每个单位一行,包含上述所有字段。
### 步骤 3阶段 1 — 生成孤立单位文档
对每个单位创建一个 .md 文件,这个阶段**只填**
- 基本信息
- 功能概述
- 设计用意
- 职责范围
- 项目内依赖
**暂不填**(留占位符 `待阶段3填写`
- 对外接口
- 使用方法
- 用例
**用 Claude Code 的做法**
```markdown
对 Claude Code 说:
"在 Document/Plugins/Item/ 下为以下每个单位创建独立的 .md 文件,
遵循 [粘贴模板]。每个单位一个文件,不要合并。
现在只填基本信息、功能概述、设计用意、职责范围、项目内依赖。
对外接口、使用方法、用例三个章节写'待阶段3填写'。"
```
然后把每个单位的结构化分析数据附在后面。
**注意**
- 每个单位**必须独立成文件**,绝对不能合并
- "设计用意"和"职责范围"是**两个独立章节**,不要偷懒合并
- "职责范围"用自然语言,**禁止**出现"职责:/不负责:"这种二分写法
- 依赖表列名必须是 `依赖项 | 关系 | 源文件`
**手动做法**
```
对清单中的每个单位:
1. 创建 Document/[模块]/[单位名].md 文件
2. 从步骤 2 的分析结果中抄入:基本信息、功能概述、设计用意、职责范围、依赖
3. 最后三节写 "待阶段3填写"
```
### 步骤 4阶段 2 — 生成关系文档
基于阶段 1 的依赖数据,为每个模块创建 `_relationships.md`
**用 Claude Code 的做法**
```markdown
对 Claude Code 说:
"为 Document/Plugins/Item/ 创建 _relationships.md。
表格列出每个文件间的 #include/forward-declare 关系。
再列出关键数据/控制流依赖链,用简单文本箭头。"
```
**手动做法**
```
1. 画一张表:
| 源文件 | 引用方式 | 目标文件 | 目标单位 |
2. 从步骤 2 的 #include/forward-declare 数据中抄入
3. 画出关键数据流:
- 创建流程:谁调用谁的什么方法创建什么
- 查询流程:谁通过谁获取什么数据
- 更新流程:数据从哪来,经过谁,到哪去
```
### 步骤 5阶段 3 — 填充接口、使用方法和用例
这是最需要"搜代码"的阶段。
**对外接口**的做法:
```
对每个 C++ 单位:
1. 打开其头文件
2. 找出所有 public/protected 方法
3. 找出所有 UFUNCTION 宏BlueprintCallable/BlueprintNativeEvent/BlueprintImplementableEvent
4. 找出所有 UPROPERTY 宏(尤其是 BlueprintReadOnly/BlueprintReadWrite 的)
5. 找出所有 DECLARE_DELEGATE / DECLARE_DYNAMIC_MULTICAST_DELEGATE
6. 用"调用者视角"重写:不要罗列函数签名,而是描述"外部代码可以做哪些事"
好例子:"调用 GetItemViews() 获取所有物品的只读视图,用于 UI 列表展示"
坏例子:"GetItemViews() const -> TArray<FItemView>"(这是罗列签名)
对每个蓝图单位:
1. 通过 MCP unreal_blueprint_query operation=inspect 查询变量和函数
2. 描述蓝图事件图中可被外部调用的函数和事件
3. 列出可编辑的实例变量
```
**使用方法**的做法:
```
对每个单位:
1. grep 搜索它在项目中的所有使用位置
rg "[单位名]" Source/ Plugins/ --type cpp --type h
2. 读相关代码段,理解它被如何使用
3. 挑选 2-5 个最典型的用法
4. 写成:文件路径:行号 — 在这段代码中是怎么用的
```
**用例**的做法:
```
对每个单位:
1. 从 grep 结果中确定哪些文件会用到它
2. 对每个使用文件写一条:文件路径:行号 — 用途说明
3. 不要伪造。如果某个文件只是 include 了但没实际用,不要写进去
```
**用 Claude Code 的做法**
```markdown
对 Claude Code 说:
"对 Document/Plugins/Item/ 下的所有 .md 文件,
用 Grep 搜索每个单位在 Source/ 和 Plugins/ 中的使用位置,
然后更新对外接口、使用方法、用例三个章节。
要求每条引用有文件路径和行号,只写真实代码中存在的用法。"
```
### 步骤 6阶段 4 — 更新 README.md
```
1. 列出所有 5 个模块
2. 每个模块下列出所有单位、文件名(带链接)、类型、父类
3. 描述文档模板的章节结构
4. 写下统计数字:多少单位、多少文件、覆盖哪些模块
```
### 步骤 7验证
```
□ 每个 .h 中定义的 class/struct/enum 都有对应的 .md 文件
□ 没有多单位合并到一个文件的情况(检查文件名列表跟单位列表一一对应)
□ 每个 .md 的"设计用意"和"职责范围"是两个独立章节
□ "职责范围"中没有出现"不负责"字样(这不是要求删除信息,而是用自然语言描述边界)
□ 每个 _relationships.md 的表格列名是"源文件|引用方式|目标文件|目标单位"
□ 每个使用方法/用例条目都带有文件路径和行号
□ 没有编造的伪代码示例
□ README.md 有完整的文档索引
□ 所有占位符"(待阶段3填写)"已被替换
```
---
## 四、工具速查
### 4.1 Grep 搜索命令
```bash
# 搜索某个类/结构体在项目中的使用
rg "FItemView" Plugins/Item/ Source/ --type-add 'ue:*.h' --type-add 'ue:*.cpp' --type ue -n
# 只列出文件名
rg "IItemContainer" -l
# 搜索 #include 关系
rg '#include.*ItemFactory' Plugins/Item/
```
### 4.2 MCP 蓝图查询命令
在 Claude Code 对话中可以直接用以下工具:
```
# 列出所有蓝图
mcp__unrealclaude__unreal_blueprint_query operation=list path_filter=/Game/Blueprints/ limit=50
# 查看单个蓝图的变量和函数
mcp__unrealclaude__unreal_blueprint_query operation=inspect
blueprint_path=/Game/Blueprints/BP_TestChar
include_variables=true
include_functions=true
# 搜索资产
mcp__unrealclaude__unreal_asset_search class_filter=Blueprint name_pattern=Test
```
### 4.3 Claude Code Task 代理
```
# 启动一个分析代理(后台运行)
Task subagent_type=Explore run_in_background=true
prompt="分析 X 目录下所有头文件..."
# 启动一个写文件的代理(后台运行)
Task subagent_type=general-purpose run_in_background=true
prompt="为以下单位创建/更新文档..."
# 查看代理输出
TaskOutput task_id=<代理ID> block=true timeout=120000
```
---
## 五、作者注
### 我是怎么被骂的
第一轮跑完「项目文档化计划」后,用户逐项检查,指出了 10 个问题(详见第一章)。核心批评:
1. **合并文件是最愚蠢的错误** — 文档的目的是让人查,文件名就是索引。把三个 struct 塞进一个 `ItemDataStructures.md`,等于给后人埋坑。
2. **"设计用意"和"职责范围"不能合并** — 前者是历史/动机,后者是边界。维护者需要知道"为什么存在",也需要知道"管多宽"。
3. **自我编造示例代码** — 我写了一堆 `// 继承 AMyActor 创建自定义 Actor` 的伪代码。用户一眼看出这不是项目里的真实代码。
4. **罗列函数签名** — 对外接口章节写成了 API reference而不是从调用者角度说明"你能干什么"。
5. **花式 ASCII 图**`_relationships.md` 里搞了一堆 ASCII 艺术框图。用户要的是简洁的文件级表格。
### 为什么这个流程可以复现
- **模板化**:每个文档的结构完全一致,没有自由发挥空间
- **可验证**每条引用必须有文件路径和行号grep 一下就能确认
- **分阶段**4 个阶段每条有明确的输入和输出,不会出现循环依赖
- **工具支持**Claude Code 的 Task 代理可以并行处理大量文件MCP 可以查询二进制蓝图
### 如果你没有 Claude Code
整个流程的核心逻辑不依赖 AI 工具。你可以纯手工完成:
1. 在 Excel/Notion 里建一张表,列出所有单位及其分析数据(步骤 2
2. 按模板逐个创建 .md 文件(步骤 3-5
3. 用 grep/VSCode 搜索来验证引用关系(步骤 7
只是一个人做 56 个文档大概需要 3-5 个工作日。
### 后续维护
代码改动后,更新对应文档:
- 新增类:按模板创建新 .md更新 `_relationships.md``README.md`
- 删除类:删除对应 .md更新 `_relationships.md``README.md`
- 修改接口:更新"对外接口"章节和对应的"使用方法"引用
- 修改依赖:更新"项目内依赖"表和 `_relationships.md`