init
This commit is contained in:
415
HOWTO_REGENERATE_DOCS.md
Normal file
415
HOWTO_REGENERATE_DOCS.md
Normal 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 或 WSL,macOS/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`
|
||||
Reference in New Issue
Block a user