本文件记录针对 NodeToCode 生成结果进行的“配置层”优化,以实现:
- 蓝图事件拆分为独立 C++ 方法,而非汇总到单一大函数;
- 自动生成类成员变量声明(含 UPROPERTY 修饰符);
- 输出必要的
#include与蓝图/资产的伪 include 注释。
-
编辑 Prompt:
Plugins/NodeToCode/Content/Prompting/CodeGen_CPP.md- 新增章节:
Event Structure Preservation - STRICT- 每个蓝图事件对应独立 C++ 方法,禁止合并为单一大函数。
- 引擎事件以
override形式生成并调用Super::。 - 自定义事件以
UFUNCTION(BlueprintCallable)形式生成,参数与引脚一一对应。 - 事件分发器使用
DECLARE_DYNAMIC_*与UPROPERTY(BlueprintAssignable),通过.Broadcast(...)触发。 .h与.cpp分别生成声明与实现。
- 新增章节:
Variable Declaration Synthesis - STRICT- 基于 Blueprint 使用自动推断并在
.h中生成UPROPERTY(...)成员(精确类型、容器、TODO 注释)。
- 基于 Blueprint 使用自动推断并在
- 新增章节:
UPROPERTY Specifiers Mapping - STRICT- 基于使用语义选择
EditAnywhere/VisibleAnywhere/BlueprintReadWrite/Transient/SaveGame等修饰符与meta标签。
- 基于使用语义选择
- 新增章节:
Includes and External References - STRICT- 在实现顶部输出必要
#include;为蓝图/资产输出// include: /Game/...伪包含注释;未知头文件输出占位注释。
- 在实现顶部输出必要
- 新增章节:
Networking & Replication - STRICT- 变量:
UPROPERTY(Replicated)/UPROPERTY(ReplicatedUsing=OnRep_Var)与OnRep_Var()生成;构造函数SetIsReplicatedByDefault(true)。 - 生命周期:声明并实现
GetLifetimeReplicatedProps(...),在.cpp使用DOREPLIFETIME(ThisClass, Var);包含#include "Net/UnrealNetwork.h"。 - RPC:按语义生成
UFUNCTION(Server|Client|NetMulticast, Reliable|Unreliable),并在实现中添加必要的权限校验(如HasAuthority())。
- 变量:
- 新增章节:
-
新增参考源文件(仅用于 LLM 参考上下文,不参与编译):
Plugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.hPlugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.cpp
-
在 Project Settings → Plugins → Node to Code 中:
- 将
Target Language设为C++ - 将
Max Translation Depth(TranslationDepth)调至1或2 - 根据偏好选择 Provider/Model(建议:Anthropic Sonnet 或 OpenAI o4-mini)
- 在
Reference Source Files中添加:Plugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.hPlugins/NodeToCode/Content/NodeToCode_Refs/N2C_EventStyleGuide.cpp
- 将
-
重新执行翻译,并在输出目录(默认
Saved/NodeToCode/Translations或自定义目录)对比结果结构。
- 多个蓝图事件将被转换为多个独立的 C++ 方法(引擎事件
BeginPlay/Tick等为override,自定义事件为UFUNCTION)。 - 事件分发器将正确以动态多播代理形式生成与触发。
- 带网络语义的变量与调用会自动输出:
Replicated/ReplicatedUsing、OnRep_、GetLifetimeReplicatedProps(含DOREPLIFETIME)、必要的#include "Net/UnrealNetwork.h",以及 Server/Client/Multicast RPC 声明与实现骨架。 - Blueprint 成员变量会在模型输入中以集中列表的形式提供,便于在同一个
.h/.cpp片段中统一生成变量声明,而不是为每个变量拆散成多个脚本片段。 - 函数局部变量现在也会被包含在模型中,以确保正确的声明和默认值。
- 使用工具栏上的
Translate Entire Blueprint时,现在会按「图」拆分为多次请求:- 事件图、每个函数图、每个宏图分别序列化为独立的 N2C JSON。
- 每个 JSON 单独发送给 LLM 服务,降低单次请求的上下文长度,避免触发 DeepSeek/OpenAI 等 Provider 的最大上下文限制错误。
- 输出落盘行为调整为“同一批次集中在一个时间戳目录下”:
- 每次执行
Translate Entire Blueprint会为当前 Blueprint 生成一个带时间戳的根目录(例如Saved/NodeToCode/Translations/MyBP_2025-11-23-16.30.00/)。 - 这一轮批次内的所有图(事件图/函数图/宏图)翻译结果都会写入该目录下的子文件夹中(按图名划分子目录),便于统一查看和管理。
- 每次执行
- C++ 生成时的文件命名规则:
- 每个图依然有自己的子目录:
<时间戳根目录>/<GraphName>/。 - 但当目标语言为 C++ 且图类型为
EventGraph时,会使用 Blueprint 类名(GraphClass/metadata.BlueprintClass)作为.h/.cpp文件名:- 例如:
AMyActor.h/AMyActor.cpp,其中会集中放置:类声明、UPROPERTY 成员、组件声明与构造、构造函数/析构函数等。
- 例如:
- 其他图(函数图/宏图等)仍旧以图名作为文件前缀,保持原有拆分粒度不变。
- 每个图依然有自己的子目录:
- 从现在开始,N2C JSON 顶层会额外包含一个
components[]数组,用于描述当前 Blueprint 中挂载的组件实例,以及相对于组件类默认值被修改过的属性:ComponentName:该组件在 Blueprint 中的变量名(例如MyMesh)。ComponentClassName:组件的类名(例如StaticMeshComponent)。AttachParentName:可选,挂载到的父组件/根(例如RootComponent或某个 SceneComponent 名)。OverriddenProperties[]:只包含那些被蓝图修改过的属性,结构与variables[]类似(Name / Type / TypeName / DefaultValue等)。
- 生成 C++ 时,LLM 会参考
components[]:- 在构造函数中使用
CreateDefaultSubobject创建对应组件,并按AttachParentName做SetupAttachment; - 对每个组件仅对
OverriddenProperties[]中出现的属性生成赋值代码(例如位置、旋转、布尔开关等),不会尝试覆盖所有引擎默认值; - 这样可以在保持上下文体积可控的前提下,尽量复现 Blueprint 中对组件默认参数的修改。
- 在构造函数中使用
- 在 CodeGen 的 Prompt 中,新增了针对 C++ 类骨架的强约束:
- 对于每个 Blueprint Class(例如
AMCMagicCardBase),要求 LLM 在生成 C++ 时:- 为该类补全/生成默认构造函数,用于创建组件(
CreateDefaultSubobject)并初始化成员变量; - 在合适的情况下生成虚析构函数,保证继承链安全;
- 对于引擎事件(
BeginPlay/Tick/OnConstruction等),使用override关键字声明,并在实现中调用Super::Xxx(...); - 对 Blueprint 自定义函数,使用
UFUNCTION(BlueprintCallable, Category="Auto")等 native 方式声明,必要时在重写父类虚函数时同样加上override。
- 为该类补全/生成默认构造函数,用于创建组件(
- 对于每个 Blueprint Class(例如
- 当用户提供已有的 .h/.cpp 文件时,LLM 会按 Prompt 要求将这些声明/实现合并进现有类,而不是重新定义一个重复的类。
Reference Source Files的示例文件仅用于提示 LLM 遵循风格,不会影响项目编译逻辑。- 如果已有你自己的事件风格示例,建议优先加入你自己的 .h/.cpp 作为参考上下文。
本节用于向社区简要说明当前针对 Blueprint → C++ 的增强设计与实现状态,便于在开源仓库中讨论与演进。
- 在
FN2CBlueprint顶层新增components[]数组,描述 Blueprint 中挂载的组件实例以及相对于组件类默认值被修改过的属性:ComponentName:组件在 Blueprint 中的变量名。ComponentClassName:组件类名(如StaticMeshComponent、TextRenderComponent等)。AttachParentName:可选,挂载到的父组件/根名称(某些版本可能为空)。OverriddenProperties[]:仅包含相对于对应组件类 CDO 被修改过的属性,结构沿用variables[](Name / Type / TypeName / DefaultValue等)。
- 在
FN2CNodeTranslator::CollectComponentOverrides中:- 遍历 Blueprint 的
SimpleConstructionScript,对每个USCS_Node的ComponentTemplate与类 CDO 做属性对比。 - 仅当某个属性相对 CDO 发生变更时,才将其写入
OverriddenProperties[],从而控制上下文长度并聚焦“真正被修改过的默认值”。
- 遍历 Blueprint 的
对应文件:Content/Prompting/CodeGen_CPP.md。
-
事件与函数结构(Event Structure Preservation - STRICT):
- 每个 Blueprint 事件转为独立 C++ 方法,禁止合并为单一大函数。
- 引擎事件统一以
override形式声明并在实现中调用Super::Xxx(...)。 - 自定义事件 / Blueprint 函数以
UFUNCTION(BlueprintCallable, Category="Auto")等 native 风格声明。 - 事件分发器使用
DECLARE_DYNAMIC_*+UPROPERTY(BlueprintAssignable),并通过.Broadcast(...)触发。 .h只放声明、.cpp只放实现,保持 Unreal 代码风格。
-
类骨架 / 构造 / 析构(Class Skeleton, Constructor, and Destructor - STRICT):
- 对每个 Blueprint 类(
metadata.BlueprintClass),要求生成或补全对应的 C++ 类:- 默认构造函数:用于创建组件并初始化成员变量。
- 适当的虚析构函数:保证继承链安全(AActor/APawn/UObject 子类)。
- 将
graph_type == "EventGraph"对应的翻译视为该 Blueprint 类的主 C++ 类骨架承载者:- 其
graphDeclaration必须包含:UCLASS()宏。class <BlueprintClass> : public <BaseClass>声明。- 成员字段,包括:
- 由顶层
variables[]推断出的 UPROPERTY 成员。 - 由
components[]推断出的组件 UPROPERTY 成员。
- 由顶层
- 所有事件 / 函数的 UFUNCTION 声明。
- 其
graphImplementation必须包含:- 构造函数实现(组件
CreateDefaultSubobject、SetupAttachment、成员初始化)。 - 需要时的析构函数实现。
- 与类级别初始化相关的其它逻辑。
- 构造函数实现(组件
- 其
- 当用户提供参考
.h/.cpp时:- 不新建重复类,而是将上述声明/实现合并进既有类体和
.cpp文件中,并补齐缺失的构造/析构/方法声明。
- 不新建重复类,而是将上述声明/实现合并进既有类体和
- 对每个 Blueprint 类(
-
变量与组件映射(Variable + Component Synthesis - STRICT):
- 顶层
variables[]:- 视为类成员变量,在
.h中生成统一的 UPROPERTY 成员区块,而不是为每个变量拆开多个脚本片段。
- 视为类成员变量,在
graphs[i].LocalVariables[]:- 视为函数局部变量,在
graphImplementation中的函数体前部声明,并尽可能根据DefaultValue进行安全初始化。
- 视为函数局部变量,在
components[]:.h中声明组件 UPROPERTY 成员(USceneComponent*/UStaticMeshComponent*/UTextRenderComponent*等)。- 构造函数中使用
CreateDefaultSubobject创建组件并根据AttachParentName调用SetupAttachment,必要时退化为挂到RootComponent。 - 仅对
OverriddenProperties[]中出现的属性生成赋值代码,优先使用语义化SetXxx接口,其次直接属性赋值。
- 顶层
-
网络与包含(Networking & Includes):
- 根据语义推断自动生成:
UPROPERTY(Replicated)/UPROPERTY(ReplicatedUsing=OnRep_)。OnRep_函数及实现。GetLifetimeReplicatedProps+DOREPLIFETIME(ThisClass, Var)。- RPC:
UFUNCTION(Server|Client|NetMulticast, Reliable|Unreliable)+ 权限检查逻辑。
- 保证
.cpp包含#include "Net/UnrealNetwork.h",并在文件顶部集中输出伪 include 注释,便于后续人工补完头文件。
- 根据语义推断自动生成:
-
批次根目录:
Translate Entire Blueprint时,仍按图拆分为多个请求,每个图各自生成一份 N2C JSON。- 同一轮请求的所有图共享一个带时间戳的根目录,例如:
Saved/NodeToCode/Translations/MyBP_2025-11-23-16.30.00/。
- 从实现层面看,会先对整个 Blueprint 调用一次
GenerateFromBlueprint生成完整的FN2CBlueprint(包含所有图以及合成出来的ClassItSelf图),然后按图切片构造“每图一个 Blueprint 视图”的 N2C JSON 发送给 LLM,因此 ClassItSelf 也会参与整蓝图批量翻译流程。
-
每个图的子目录与文件名:
- 每个 graph 有自己的子目录:
<时间戳根目录>/<GraphName>/,内部存放该图的.h/.cpp以及_Notes.txt等辅助文件。 - 实际写盘时会对
GraphName做一次轻量的文件名安全清洗:- 去掉首尾空格;
- 将 Windows 不支持的文件名字符(如
< > : " / \\ | ? *等)替换为下划线_; - JSON 中的
graph_name保持原样不变,仅文件系统路径使用清洗后的名字。
- 当前实现中,无论图类型(EventGraph/Function/Macro 等),生成的 C++ 文件均使用清洗后的
GraphName作为文件名前缀:- 例如:
EventGraph/EventGraph.h、SomeFunction/SomeFunction.cpp。
- 例如:
- 对于 C++ 的
ClassItSelf图(graph_type == "ClassItSelf")且其graph_class非空时,除了按图输出ClassItSelf/ClassItSelf.*以外,还会额外生成一对以类名命名的脚本:<时间戳根目录>/<GraphClass>/<GraphClass>.h<时间戳根目录>/<GraphClass>/<GraphClass>.cpp
- 这两份类名脚本仅承载类骨架:
- UCLASS 与完整 class 声明;
- 所有 Blueprint 变量对应的 UPROPERTY 成员;
- 所有 Blueprint 组件(来自
components[])对应的 UPROPERTY 组件指针成员; - 构造/析构函数声明与实现;
- 在构造函数中通过
CreateDefaultSubobject创建组件、根据AttachParentName调用SetupAttachment、以及对OverriddenProperties[]中属性的初始化逻辑。
- 组件的 UPROPERTY 成员与构造阶段的创建/挂接/属性初始化只会出现在
<GraphClass>.h/.cpp这对类骨架文件中,不会出现在EventGraph等其他按图拆分的.h/.cpp里; EventGraph图本身只负责事件/函数的声明与实现,不再在类目录下生成额外副本,从而保持“类结构 + 组件 + 构造/析构”和“事件逻辑”在输出层面上的清晰分离。
- 每个 graph 有自己的子目录:
- 目前 Prompt 中对事件与函数声明风格是“写死”的:
- 引擎事件统一使用
override。 - 自定义函数统一使用
UFUNCTION(BlueprintCallable, Category="Auto")等 native 风格。
- 引擎事件统一使用
- 计划在
UN2CSettings中增加配置项(例如bUseNativeOverrideStyleForFunctions或EN2CFunctionStyle),并在 Prompt 组装时读取:- 开启:维持当前“强 native + override”风格。
- 关闭:使用更简化的函数声明风格,弱化
override/UFUNCTION约束,方便某些项目做轻量级迁移。
这一系列设计在不改变 NodeToCode 核心数据模型(FN2CBlueprint/FN2CGraph)的前提下,通过 Prompt 约定与落盘命名策略,将“类骨架 + 组件 + 构造/析构”聚合到以 Blueprint 类名命名的 C++ 脚本中,同时保留按图拆分的优势,便于后续社区在此基础上迭代。