Unreal5 研究笔记 蓝图自定义节点

环境:Unreal5.6 语言:C++

面向:Unreal初学者

总起

蓝图在Unreal中非常重要,基本是个必学概念,它分别承担了以下的功能:

  1. 游戏逻辑编写(类似Unity中的C#脚本),拥有定义或调用事件、函数、变量等能力;
  2. 可复用的预制件(类似Unity中的Prefab);
  3. 动画蓝图(类似于Unity中的Animator);
  4. 关卡蓝图,这个其实没什么特别的,就是每个关卡独有的全局事件列表;
  5. 控件蓝图,将UI视觉元素与交互逻辑整合在一起。

蓝图的前身是Kismet,Unreal3时期功能较为有限,逻辑大部分还使用Unreal Script来编写。到了Unreal4,蓝图正式推出,整合了前两者的能力。

因此如果程序员想脱离蓝图,纯用C++开发会痛苦无比。

当然最近我有了解到现在AI编程针对蓝图的适配有限(因为现在的AI编程本质就是做文本分析嘛),所以有人尝试让AI直接生成C++代码,似乎结果还不错,不过C++这个编译使用起来还是比较麻烦的......(想念C# 1秒钟)

自定义蓝图节点

说回到当前的一个需求:想要定义一个两个Int值相加的节点。

首先Unreal中本身自带了一个 Add 节点,并且支持任意类型的相加:

其次是使用 UFUNCTION,直接在C++中定义一个函数,这应该最常用的方式了:

cpp 复制代码
// .h文件
UFUNCTION(BlueprintPure, Category = "MyTestActor|Math", meta = (DisplayName = "Actor Static Add"))
static int32 StaticMyAddInts(int32 a, int32 b);

// .cpp文件
int32 AMyTestActor::StaticMyAddInts(int32 a, int32 b)
{
	return a + b;
}

这里需要注意一些细节:

  1. BlueprintPure,无执行引脚,需要有返回值(区别于BlueprintCallable有执行引脚);
  2. Category描述是右键添加节点时的分类;
  3. DisplayName就是显示的名字。

结果:

最后是继承于 K2Node,完全自定义节点,灵活度最高,也是最麻烦的一种。好处是可以实现动态引脚、完全自定义外观、自定义编译时展开等。

cpp 复制代码
// .h文件
class MYEDITOR_API UMyBPNode_AddTwoInts : public UK2Node
{
	GENERATED_BODY()

public:
    // 纯节点(无执行引脚)
    virtual bool IsNodePure() const override { return true; }

    // 创建引脚
    virtual void AllocateDefaultPins() override;

    // 节点外观
    virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
    virtual FText GetTooltipText() const override;

    // 菜单栏的注册
    virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
    virtual FText GetMenuCategory() const override;

    // 编译时展开
    virtual void ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
};

// .cpp文件(样式类的就不做多过多赘述,主要是展开节点)
void UMyBPNode_AddTwoInts::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
    Super::ExpandNode(CompilerContext, SourceGraph);

    // 1. 找到目标函数:UKismetMathLibrary::Add_IntInt
    UFunction* AddFunction = UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_FUNCTION_NAME_CHECKED(UKismetMathLibrary, Add_IntInt));
    if (!AddFunction)
    {
        CompilerContext.MessageLog.Error(*LOCTEXT("AddFunctionNotFound", "Add_IntInt function not found.").ToString(), this);
        return;
    }

    // 2. 在编译图中生成一个中间函数调用节点
    UK2Node_CallFunction* CallAddNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
    CallAddNode->SetFromFunction(AddFunction);
    CallAddNode->AllocateDefaultPins();

    // 3. 获取原节点和中间节点的引脚
    UEdGraphPin* InputAPin = FindPinChecked(TEXT("A"));
    UEdGraphPin* InputBPin = FindPinChecked(TEXT("B"));
    UEdGraphPin* ResultPin = FindPinChecked(TEXT("Result"));

    UEdGraphPin* CallInputAPin = CallAddNode->FindPinChecked(TEXT("A"));
    UEdGraphPin* CallInputBPin = CallAddNode->FindPinChecked(TEXT("B"));
    UEdGraphPin* CallReturnPin = CallAddNode->GetReturnValuePin();

    // 4. 将原节点的引脚链接转移到中间节点
    CompilerContext.MovePinLinksToIntermediate(*InputAPin, *CallInputAPin);
    CompilerContext.MovePinLinksToIntermediate(*InputBPin, *CallInputBPin);
    CompilerContext.MovePinLinksToIntermediate(*ResultPin, *CallReturnPin);

    // 5. 断开原节点的所有链接(编译器将忽略它)
    BreakAllNodeLinks();
}

这种方式定义的节点类主要存在于编辑器和编译期,给Kismet进行编译使用,上述两数相加的节点便不会打到游戏中,取而代之的是编译时的展开(ExpandNode),将自定义的函数放到UK2Node_CallFunction 的一个中间节点中,然后将现有的连接转移到这种中间节点上。

结果:

如果想要做到像Unreal中添加任意类型的端口,则需要使用到wildcard端口。

定义的这个类应该需要放在UncookedOnly 模块中。其他的模块类型:

  1. Runtime,游戏的核心逻辑,在任意情况下都会加载,一般Unreal工程默认创建的模块就是Runtime的;
  2. Editor,纯编辑器专用的,与UncookedOnly的区别在于这个Cook的过程:Cook过程会将Editor数据转换成运行时数据,此时如果需要这个过程的选择UncookedOnly,不需要选Editor即可。

想要创建模块的话:创建模块文件夹,在下面创建模块名.Build.cs、模块名.cpp、模块名.h文件,然后在 项目名.uproject、项目名.Target.cs文件中注册。(具体操作就不赘述了,另外如果想要做成Plugin则不需要处理项目名.Target.cs,当然创建Plugin引擎内可以通过Edit -> Plugins -> Add创建)

相关推荐
中屹指纹浏览器1 小时前
2026指纹浏览器行为指纹对抗技术详解:从算法识别到真人模拟全方案
经验分享·笔记
2301_809051141 小时前
Linux 数据库开发 学习笔记
笔记·学习·数据库开发
很楠爱上2 小时前
Node.js 模块化学习笔记
笔记·学习·node.js
mnasd2 小时前
RockyLinux 9.5 部署 Kubernetes1.35 集群
笔记
東隅已逝,桑榆非晚2 小时前
编译和链接
c语言·笔记
05候补工程师2 小时前
【考研高数核心突破】极限的本质、高频解题套路与海涅定理深度解析(附经典例题思维导图式拆解)
经验分享·笔记·考研·算法
智者知已应修善业2 小时前
【51单片机8个LED的花样12亮34熄56间隔78闪烁3秒3闪烁】2023-11-4
c++·经验分享·笔记·算法·51单片机
Upsy-Daisy3 小时前
IOTA 学习笔记(六):Move 语言入门
笔记·学习
searchforAI3 小时前
网盘视频转文字后,如何高效做笔记并长期归档?
人工智能·笔记·学习·ai·音视频·语音识别·网盘
土狗TuGou3 小时前
SQL内功笔记 · 第9篇:UPDATE FROM 进阶——告别逐行子查询,拥抱集合更新
java·数据库·笔记·sql·mysql