|
| 1 | +# NodeToCode 配置层优化变更说明 |
| 2 | + |
| 3 | +本文件记录针对 NodeToCode 生成结果进行的“配置层”优化,以实现: |
| 4 | +1) 蓝图事件拆分为独立 C++ 方法,而非汇总到单一大函数; |
| 5 | +2) 自动生成类成员变量声明(含 UPROPERTY 修饰符); |
| 6 | +3) 输出必要的 `#include` 与蓝图/资产的伪 include 注释。 |
| 7 | + |
| 8 | +## 变更内容 |
| 9 | +- 编辑 Prompt:`Plugins/NodeToCode/Content/Prompting/CodeGen_CPP.md` |
| 10 | + - 新增章节:`Event Structure Preservation - STRICT` |
| 11 | + - 每个蓝图事件对应独立 C++ 方法,禁止合并为单一大函数。 |
| 12 | + - 引擎事件以 `override` 形式生成并调用 `Super::`。 |
| 13 | + - 自定义事件以 `UFUNCTION(BlueprintCallable)` 形式生成,参数与引脚一一对应。 |
| 14 | + - 事件分发器使用 `DECLARE_DYNAMIC_*` 与 `UPROPERTY(BlueprintAssignable)`,通过 `.Broadcast(...)` 触发。 |
| 15 | + - `.h` 与 `.cpp` 分别生成声明与实现。 |
| 16 | + - 新增章节:`Variable Declaration Synthesis - STRICT` |
| 17 | + - 基于 Blueprint 使用自动推断并在 `.h` 中生成 `UPROPERTY(...)` 成员(精确类型、容器、TODO 注释)。 |
| 18 | + - 新增章节:`UPROPERTY Specifiers Mapping - STRICT` |
| 19 | + - 基于使用语义选择 `EditAnywhere/VisibleAnywhere/BlueprintReadWrite/Transient/SaveGame` 等修饰符与 `meta` 标签。 |
| 20 | + - 新增章节:`Includes and External References - STRICT` |
| 21 | + - 在实现顶部输出必要 `#include`;为蓝图/资产输出 `// include: /Game/...` 伪包含注释;未知头文件输出占位注释。 |
| 22 | + - 新增章节:`Networking & Replication - STRICT` |
| 23 | + - 变量:`UPROPERTY(Replicated)` / `UPROPERTY(ReplicatedUsing=OnRep_Var)` 与 `OnRep_Var()` 生成;构造函数 `SetIsReplicatedByDefault(true)`。 |
| 24 | + - 生命周期:声明并实现 `GetLifetimeReplicatedProps(...)`,在 `.cpp` 使用 `DOREPLIFETIME(ThisClass, Var)`;包含 `#include "Net/UnrealNetwork.h"`。 |
| 25 | + - RPC:按语义生成 `UFUNCTION(Server|Client|NetMulticast, Reliable|Unreliable)`,并在实现中添加必要的权限校验(如 `HasAuthority()`)。 |
| 26 | + |
| 27 | +- 新增参考源文件(仅用于 LLM 参考上下文,不参与编译): |
| 28 | + - `Plugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.h` |
| 29 | + - `Plugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.cpp` |
| 30 | + |
| 31 | +## 使用步骤 |
| 32 | +1. 在 Project Settings → Plugins → Node to Code 中: |
| 33 | + - 将 `Target Language` 设为 `C++` |
| 34 | + - 将 `Max Translation Depth`(TranslationDepth)调至 `1` 或 `2` |
| 35 | + - 根据偏好选择 Provider/Model(建议:Anthropic Sonnet 或 OpenAI o4-mini) |
| 36 | + - 在 `Reference Source Files` 中添加: |
| 37 | + - `Plugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.h` |
| 38 | + - `Plugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.cpp` |
| 39 | + |
| 40 | +2. 重新执行翻译,并在输出目录(默认 `Saved/NodeToCode/Translations` 或自定义目录)对比结果结构。 |
| 41 | + |
| 42 | +## 期望效果 |
| 43 | +- 多个蓝图事件将被转换为多个独立的 C++ 方法(引擎事件 `BeginPlay`/`Tick` 等为 `override`,自定义事件为 `UFUNCTION`)。 |
| 44 | +- 事件分发器将正确以动态多播代理形式生成与触发。 |
| 45 | +- 带网络语义的变量与调用会自动输出:`Replicated/ReplicatedUsing`、`OnRep_`、`GetLifetimeReplicatedProps`(含 `DOREPLIFETIME`)、必要的 `#include "Net/UnrealNetwork.h"`,以及 Server/Client/Multicast RPC 声明与实现骨架。 |
| 46 | +- Blueprint 成员变量会在模型输入中以集中列表的形式提供,便于在同一个 `.h`/`.cpp` 片段中统一生成变量声明,而不是为每个变量拆散成多个脚本片段。 |
| 47 | +- 函数局部变量现在也会被包含在模型中,以确保正确的声明和默认值。 |
| 48 | + |
| 49 | +## Translate Entire Blueprint 行为说明 |
| 50 | + |
| 51 | +- 使用工具栏上的 `Translate Entire Blueprint` 时,现在会按「图」拆分为多次请求: |
| 52 | + - 事件图、每个函数图、每个宏图分别序列化为独立的 N2C JSON。 |
| 53 | + - 每个 JSON 单独发送给 LLM 服务,降低单次请求的上下文长度,避免触发 DeepSeek/OpenAI 等 Provider 的最大上下文限制错误。 |
| 54 | +- 输出落盘行为调整为“同一批次集中在一个时间戳目录下”: |
| 55 | + - 每次执行 `Translate Entire Blueprint` 会为当前 Blueprint 生成一个带时间戳的根目录(例如 `Saved/NodeToCode/Translations/MyBP_2025-11-23-16.30.00/`)。 |
| 56 | + - 这一轮批次内的所有图(事件图/函数图/宏图)翻译结果都会写入该目录下的子文件夹中(按图名划分子目录),便于统一查看和管理。 |
| 57 | + - C++ 生成时的文件命名规则: |
| 58 | + - 每个图依然有自己的子目录:`<时间戳根目录>/<GraphName>/`。 |
| 59 | + - 但当目标语言为 C++ 且图类型为 `EventGraph` 时,会使用 Blueprint 类名(`GraphClass`/`metadata.BlueprintClass`)作为 `.h/.cpp` 文件名: |
| 60 | + - 例如:`AMyActor.h` / `AMyActor.cpp`,其中会集中放置:类声明、UPROPERTY 成员、组件声明与构造、构造函数/析构函数等。 |
| 61 | + - 其他图(函数图/宏图等)仍旧以图名作为文件前缀,保持原有拆分粒度不变。 |
| 62 | + |
| 63 | +## Blueprint 组件(Components)默认值覆盖 |
| 64 | + |
| 65 | +- 从现在开始,N2C JSON 顶层会额外包含一个 `components[]` 数组,用于描述当前 Blueprint 中挂载的组件实例,以及**相对于组件类默认值被修改过的属性**: |
| 66 | + - `ComponentName`:该组件在 Blueprint 中的变量名(例如 `MyMesh`)。 |
| 67 | + - `ComponentClassName`:组件的类名(例如 `StaticMeshComponent`)。 |
| 68 | + - `AttachParentName`:可选,挂载到的父组件/根(例如 `RootComponent` 或某个 SceneComponent 名)。 |
| 69 | + - `OverriddenProperties[]`:只包含那些**被蓝图修改过**的属性,结构与 `variables[]` 类似(`Name / Type / TypeName / DefaultValue` 等)。 |
| 70 | +- 生成 C++ 时,LLM 会参考 `components[]`: |
| 71 | + - 在构造函数中使用 `CreateDefaultSubobject` 创建对应组件,并按 `AttachParentName` 做 `SetupAttachment`; |
| 72 | + - 对每个组件仅对 `OverriddenProperties[]` 中出现的属性生成赋值代码(例如位置、旋转、布尔开关等),不会尝试覆盖所有引擎默认值; |
| 73 | + - 这样可以在保持上下文体积可控的前提下,尽量复现 Blueprint 中对组件默认参数的修改。 |
| 74 | + |
| 75 | +## C++ 生成约定补充(构造函数 / 析构函数 / override) |
| 76 | + |
| 77 | +- 在 CodeGen 的 Prompt 中,新增了针对 C++ 类骨架的强约束: |
| 78 | + - 对于每个 Blueprint Class(例如 `AMCMagicCardBase`),要求 LLM 在生成 C++ 时: |
| 79 | + - 为该类补全/生成默认构造函数,用于创建组件(`CreateDefaultSubobject`)并初始化成员变量; |
| 80 | + - 在合适的情况下生成虚析构函数,保证继承链安全; |
| 81 | + - 对于引擎事件(`BeginPlay` / `Tick` / `OnConstruction` 等),使用 `override` 关键字声明,并在实现中调用 `Super::Xxx(...)`; |
| 82 | + - 对 Blueprint 自定义函数,使用 `UFUNCTION(BlueprintCallable, Category="Auto")` 等 native 方式声明,必要时在重写父类虚函数时同样加上 `override`。 |
| 83 | +- 当用户提供已有的 .h/.cpp 文件时,LLM 会按 Prompt 要求将这些声明/实现**合并进现有类**,而不是重新定义一个重复的类。 |
| 84 | + |
| 85 | +## 备注 |
| 86 | +- `Reference Source Files` 的示例文件仅用于提示 LLM 遵循风格,不会影响项目编译逻辑。 |
| 87 | +- 如果已有你自己的事件风格示例,建议优先加入你自己的 .h/.cpp 作为参考上下文。 |
| 88 | + |
| 89 | +## C++ 生成增强方案与设计说明(对社区贡献摘要) |
| 90 | + |
| 91 | +本节用于向社区简要说明当前针对 Blueprint → C++ 的增强设计与实现状态,便于在开源仓库中讨论与演进。 |
| 92 | + |
| 93 | +### 1. N2C JSON 模型扩展:components[](已实现) |
| 94 | + |
| 95 | +- 在 `FN2CBlueprint` 顶层新增 `components[]` 数组,描述 Blueprint 中挂载的组件实例以及**相对于组件类默认值被修改过的属性**: |
| 96 | + - `ComponentName`:组件在 Blueprint 中的变量名。 |
| 97 | + - `ComponentClassName`:组件类名(如 `StaticMeshComponent`、`TextRenderComponent` 等)。 |
| 98 | + - `AttachParentName`:可选,挂载到的父组件/根名称(某些版本可能为空)。 |
| 99 | + - `OverriddenProperties[]`:仅包含相对于对应组件类 CDO 被修改过的属性,结构沿用 `variables[]`(`Name / Type / TypeName / DefaultValue` 等)。 |
| 100 | +- 在 `FN2CNodeTranslator::CollectComponentOverrides` 中: |
| 101 | + - 遍历 Blueprint 的 `SimpleConstructionScript`,对每个 `USCS_Node` 的 `ComponentTemplate` 与类 CDO 做属性对比。 |
| 102 | + - 仅当某个属性相对 CDO 发生变更时,才将其写入 `OverriddenProperties[]`,从而控制上下文长度并聚焦“真正被修改过的默认值”。 |
| 103 | + |
| 104 | +### 2. Prompt 侧 C++ 生成约束(已实现) |
| 105 | + |
| 106 | +对应文件:`Content/Prompting/CodeGen_CPP.md`。 |
| 107 | + |
| 108 | +- **事件与函数结构(Event Structure Preservation - STRICT)**: |
| 109 | + - 每个 Blueprint 事件转为独立 C++ 方法,禁止合并为单一大函数。 |
| 110 | + - 引擎事件统一以 `override` 形式声明并在实现中调用 `Super::Xxx(...)`。 |
| 111 | + - 自定义事件 / Blueprint 函数以 `UFUNCTION(BlueprintCallable, Category="Auto")` 等 native 风格声明。 |
| 112 | + - 事件分发器使用 `DECLARE_DYNAMIC_*` + `UPROPERTY(BlueprintAssignable)`,并通过 `.Broadcast(...)` 触发。 |
| 113 | + - `.h` 只放声明、`.cpp` 只放实现,保持 Unreal 代码风格。 |
| 114 | + |
| 115 | +- **类骨架 / 构造 / 析构(Class Skeleton, Constructor, and Destructor - STRICT)**: |
| 116 | + - 对每个 Blueprint 类(`metadata.BlueprintClass`),要求生成或补全对应的 C++ 类: |
| 117 | + - 默认构造函数:用于创建组件并初始化成员变量。 |
| 118 | + - 适当的虚析构函数:保证继承链安全(AActor/APawn/UObject 子类)。 |
| 119 | + - 将 `graph_type == "EventGraph"` 对应的翻译视为**该 Blueprint 类的主 C++ 类骨架承载者**: |
| 120 | + - 其 `graphDeclaration` 必须包含: |
| 121 | + - `UCLASS()` 宏。 |
| 122 | + - `class <BlueprintClass> : public <BaseClass>` 声明。 |
| 123 | + - 成员字段,包括: |
| 124 | + - 由顶层 `variables[]` 推断出的 UPROPERTY 成员。 |
| 125 | + - 由 `components[]` 推断出的组件 UPROPERTY 成员。 |
| 126 | + - 所有事件 / 函数的 UFUNCTION 声明。 |
| 127 | + - 其 `graphImplementation` 必须包含: |
| 128 | + - 构造函数实现(组件 `CreateDefaultSubobject`、`SetupAttachment`、成员初始化)。 |
| 129 | + - 需要时的析构函数实现。 |
| 130 | + - 与类级别初始化相关的其它逻辑。 |
| 131 | + - 当用户提供参考 `.h/.cpp` 时: |
| 132 | + - 不新建重复类,而是将上述声明/实现合并进既有类体和 `.cpp` 文件中,并补齐缺失的构造/析构/方法声明。 |
| 133 | + |
| 134 | +- **变量与组件映射(Variable + Component Synthesis - STRICT)**: |
| 135 | + - 顶层 `variables[]`: |
| 136 | + - 视为类成员变量,在 `.h` 中生成统一的 UPROPERTY 成员区块,而不是为每个变量拆开多个脚本片段。 |
| 137 | + - `graphs[i].LocalVariables[]`: |
| 138 | + - 视为函数局部变量,在 `graphImplementation` 中的函数体前部声明,并尽可能根据 `DefaultValue` 进行安全初始化。 |
| 139 | + - `components[]`: |
| 140 | + - `.h` 中声明组件 UPROPERTY 成员(`USceneComponent*` / `UStaticMeshComponent*` / `UTextRenderComponent*` 等)。 |
| 141 | + - 构造函数中使用 `CreateDefaultSubobject` 创建组件并根据 `AttachParentName` 调用 `SetupAttachment`,必要时退化为挂到 `RootComponent`。 |
| 142 | + - 仅对 `OverriddenProperties[]` 中出现的属性生成赋值代码,优先使用语义化 `SetXxx` 接口,其次直接属性赋值。 |
| 143 | + |
| 144 | +- **网络与包含(Networking & Includes)**: |
| 145 | + - 根据语义推断自动生成: |
| 146 | + - `UPROPERTY(Replicated)` / `UPROPERTY(ReplicatedUsing=OnRep_)`。 |
| 147 | + - `OnRep_` 函数及实现。 |
| 148 | + - `GetLifetimeReplicatedProps` + `DOREPLIFETIME(ThisClass, Var)`。 |
| 149 | + - RPC:`UFUNCTION(Server|Client|NetMulticast, Reliable|Unreliable)` + 权限检查逻辑。 |
| 150 | + - 保证 `.cpp` 包含 `#include "Net/UnrealNetwork.h"`,并在文件顶部集中输出伪 include 注释,便于后续人工补完头文件。 |
| 151 | + |
| 152 | +### 3. 输出目录与 C++ 文件命名(已实现) |
| 153 | + |
| 154 | +- **批次根目录**: |
| 155 | + - `Translate Entire Blueprint` 时,仍按图拆分为多个请求,每个图各自生成一份 N2C JSON。 |
| 156 | + - 同一轮请求的所有图共享一个带时间戳的根目录,例如: |
| 157 | + - `Saved/NodeToCode/Translations/MyBP_2025-11-23-16.30.00/`。 |
| 158 | + - 从实现层面看,会先对整个 Blueprint 调用一次 `GenerateFromBlueprint` 生成完整的 `FN2CBlueprint`(包含所有图以及合成出来的 `ClassItSelf` 图),然后按图切片构造“每图一个 Blueprint 视图”的 N2C JSON 发送给 LLM,因此 ClassItSelf 也会参与整蓝图批量翻译流程。 |
| 159 | + |
| 160 | +- **每个图的子目录与文件名**: |
| 161 | + - 每个 graph 有自己的子目录:`<时间戳根目录>/<GraphName>/`,内部存放该图的 `.h/.cpp` 以及 `_Notes.txt` 等辅助文件。 |
| 162 | + - 实际写盘时会对 `GraphName` 做一次**轻量的文件名安全清洗**: |
| 163 | + - 去掉首尾空格; |
| 164 | + - 将 Windows 不支持的文件名字符(如 `< > : " / \\ | ? *` 等)替换为下划线 `_`; |
| 165 | + - JSON 中的 `graph_name` 保持原样不变,仅文件系统路径使用清洗后的名字。 |
| 166 | + - 当前实现中,无论图类型(EventGraph/Function/Macro 等),生成的 C++ 文件均使用**清洗后的 `GraphName`** 作为文件名前缀: |
| 167 | + - 例如:`EventGraph/EventGraph.h`、`SomeFunction/SomeFunction.cpp`。 |
| 168 | + - 对于 C++ 的 `ClassItSelf` 图(`graph_type == "ClassItSelf"`)且其 `graph_class` 非空时,除了按图输出 `ClassItSelf/ClassItSelf.*` 以外,还会额外生成一对以类名命名的脚本: |
| 169 | + - `<时间戳根目录>/<GraphClass>/<GraphClass>.h` |
| 170 | + - `<时间戳根目录>/<GraphClass>/<GraphClass>.cpp` |
| 171 | + - 这两份类名脚本仅承载类骨架: |
| 172 | + - UCLASS 与完整 class 声明; |
| 173 | + - 所有 Blueprint 变量对应的 UPROPERTY 成员; |
| 174 | + - 所有 Blueprint 组件(来自 `components[]`)对应的 UPROPERTY 组件指针成员; |
| 175 | + - 构造/析构函数声明与实现; |
| 176 | + - 在构造函数中通过 `CreateDefaultSubobject` 创建组件、根据 `AttachParentName` 调用 `SetupAttachment`、以及对 `OverriddenProperties[]` 中属性的初始化逻辑。 |
| 177 | + - 组件的 UPROPERTY 成员与构造阶段的创建/挂接/属性初始化**只会出现在 `<GraphClass>.h/.cpp` 这对类骨架文件中**,不会出现在 `EventGraph` 等其他按图拆分的 `.h/.cpp` 里; |
| 178 | + - `EventGraph` 图本身只负责事件/函数的声明与实现,不再在类目录下生成额外副本,从而保持“类结构 + 组件 + 构造/析构”和“事件逻辑”在输出层面上的清晰分离。 |
| 179 | + |
| 180 | +### 4. override/native 风格可配置化(规划中) |
| 181 | + |
| 182 | +- 目前 Prompt 中对事件与函数声明风格是“写死”的: |
| 183 | + - 引擎事件统一使用 `override`。 |
| 184 | + - 自定义函数统一使用 `UFUNCTION(BlueprintCallable, Category="Auto")` 等 native 风格。 |
| 185 | +- 计划在 `UN2CSettings` 中增加配置项(例如 `bUseNativeOverrideStyleForFunctions` 或 `EN2CFunctionStyle`),并在 Prompt 组装时读取: |
| 186 | + - 开启:维持当前“强 native + override”风格。 |
| 187 | + - 关闭:使用更简化的函数声明风格,弱化 `override`/`UFUNCTION` 约束,方便某些项目做轻量级迁移。 |
| 188 | + |
| 189 | +这一系列设计在不改变 NodeToCode 核心数据模型(`FN2CBlueprint`/`FN2CGraph`)的前提下,通过 Prompt 约定与落盘命名策略,将“类骨架 + 组件 + 构造/析构”聚合到以 Blueprint 类名命名的 C++ 脚本中,同时保留按图拆分的优势,便于后续社区在此基础上迭代。 |
0 commit comments