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创建)

相关推荐
INS_KF7 小时前
【编程笔记】成员函数中两个 const 的区别(const Data &getData() const;)
前端·javascript·笔记
tju新生代魔迷8 小时前
Verilog HDL 学习笔记(十三)| 第13章 编程语言接口(PLI)
笔记·学习
点心的游戏开发世界9 小时前
UnityShaderGraph滚动UV笔记
笔记·uv
瑞码空间10 小时前
git知识点黄金笔记
笔记·git·elasticsearch
天若有情67311 小时前
Node+MySQL小型全栈笔记项目实战课程分享
数据库·笔记·mysql
摇滚侠13 小时前
《SpringBoot 3:入门与应用实战》第 12 章 JDBC 与事务 JDBC 事务管理 阅读笔记 33
spring boot·笔记·后端
Wendy不吃榴莲14 小时前
# AI短剧教程 - 《后西游记》开播后,AI影视为什么更考验“连续讲故事”?
人工智能·笔记·学习·ai·视频
Agudamu116114 小时前
B站学习视频怎么变笔记:用 Ai好记 + Obsidian 搭建个人知识库的完整教程
人工智能·笔记·学习·音视频
疯狂打码的少年16 小时前
【数据库技术】SQL嵌套查询(IN/EXISTS/ANY/ALL)
数据库·笔记·sql
凯尔萨厮17 小时前
Java学习笔记十四(网络通信)
笔记·学习