4.4 蓝图节点扩展与 C++ 库导出
让非程序员用上你的 C++ 能力
工具的核心逻辑常是 C++(性能/API 需要),但又想让策划在蓝图里用起来。三条标准路线:
| 方式 | 场景 | 例子 |
|---|---|---|
| 蓝图函数库(UBlueprintFunctionLibrary) | 无状态工具函数 | UMyToolBPLibrary::BatchValidate() |
| 蓝图资产操作节点 | Editor Scripting 那类 | 已有官方实现,多数不用自己做 |
| 派生 UObject 带蓝图可调用方法 | 有状态对象 | 数据处理管线对象 |
UCLASS()
class UMyToolBPLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, Category = "MyTool")
static TArray<FString> FindBrokenAssets(const FString& RootPath);
};
这是「C++ 做重活、蓝图做交互」的标准接口,与 3.4 的混合策略天然衔接。
参数类型的选择
蓝图节点参数有边界,选类型时记住:
- 支持:基础类型、FString/FText/FName、UObject*、结构体(USTRUCT(BlueprintType)、枚举(UENUM(BlueprintType)、数组/集合
- 不支持/麻烦:指针的指针、void*、模板类、std:: 容器、TMap 的蓝图支持有限
- 可选:蓝图里参数默认值/高级显示(advanced display)
USTRUCT 的蓝图友好性
USTRUCT(BlueprintType)
struct FAssetCheckResult
{
GENERATED_BODY()
UPROPERTY(BlueprintReadWrite, Category="MyTool") TArray<FString> BrokenAssets;
UPROPERTY(BlueprintReadWrite, Category="MyTool") int32 TotalChecked = 0;
};
注意:结构体的成员变化是「不兼容旧存档/旧蓝图引脚」的破坏性改动。给结构体留扩展空间(比如额外的 Reserved 字段)是坏味道的设计——对外暴露的结构体就按新版本处理,配合版本号管理。
蓝图异步节点
如果有耗时操作想从蓝图触发且不卡:
UCLASS()
class UMyAsyncTask : public UBlueprintAsyncActionBase
{
GENERATED_BODY()
public:
UFUNCTION(BlueprintCallable, meta=(BlueprintInternalUseOnly="true"))
static UMyAsyncTask* RunCheck();
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnDone);
UPROPERTY(BlueprintAssignable) FOnDone OnDone;
virtual void Activate() override; // 在这起后台任务
};
蓝图里 spawn 出来后 bind OnDone 事件。这套模式适合「处理几万个资产」之类的场景。
函数命名与兼容
暴露给蓝图的函数一旦发布,参数和返回值类型就不能随便改(蓝图引脚会断)。规范:
- 新功能 → 加新函数名
- 改行为 → 加版本后缀(
FindBrokenAssets2)或新参数且必须给默认值 - 废弃函数 → 留着,EditorOnly 或标记 deprecated 提示
命名空间与 C++ 导出
模块对外暴露 C++ API(让别的插件/项目 C++ 直接调):类放 Public 目录、用 MODULENAME_API 宏。约定:插件的公开 C++ API 比蓝图 API 更难改——提前把「核心稳定接口」和「内部工具实现」分开放。
心得:把蓝图 API 当「产品 API」对待。我们插件的核心蓝图函数三年没改签名——不是需求没变,是每次变都加新函数而不是改旧的。生态的稳定感就是这么积累的。
