以上图为例,一个蓝图节点从外观上看有四个部分
Node TitleTool Tip所以 UK2Node 提供了一些函数能够表现出这些内容
| 函数名 | 作用 |
|---|---|
| AllocateDefaultPins() | 在这个函数中,你将为你的节点分配默认的输入和输出引脚 |
| GetMenuActions() | 这个函数用于将你的自定义节点添加到蓝图编辑器的节点菜单中 |
| GetNodeTitle() | 返回节点在蓝图编辑器中显示的标题 |
| GetTooltipText() | 提供当鼠标悬停在节点上时显示的工具提示文本 |
| GetMenuCategory() | 定义节点在菜单中的分类 |
| ExpandNode() | 如果你的节点需要在编译时执行特殊的行为,你可以在这个函数中实现 |
设置节点的 Title 和 Tip
FText GetTooltipText() const override { return FText::FromString(TEXT("a custom node")); }
FText GetNodeTitle(ENodeTitleType::Type TitleType) const override { return FText::FromString(TEXT("Get Variant Data By Type")); }
使用 AllocateDefaultPins 来创建节点的输入输出针脚
#define EnumPinName TEXT("Enum Type")
#define OutputPinName TEXT("Out Value")
#define InputPinName TEXT("In Data")
void UVariantNode::AllocateDefaultPins()
{
// 获取关联的枚举类型
EnumType = FindObject<UEnum>(ANY_PACKAGE, *VariantHelper::EnumName);
// Create the output pin
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, OutputPinName);
// Create the input pin
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Byte, EnumType, EnumPinName);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, InputPinName);
}
重写 GetMenuCategory 可以设置节点所属类型
FText GetMenuCategory() const override { return FText::FromString(TEXT("BlueprintVariantNodes")); }
这个节点的作用能够根据输入的 Enum Type 类型,能够修改 Out Value 输出类型
当输入针脚的值发生改变的时候会触发 PinDefaultValueChanged 函数,该函数定义在 UK2Node 的父类 UEdGraphNode 中
UENUM(BlueprintType)
enum class EPARMS_TYPE :uint8
{
EPARMS_TYPE_DEFAULT = 0 UMETA(DisplayName = "Default"),
EPARMS_TYPE_BOOL = 2 UMETA(DisplayName = "Bool"),
EPARMS_TYPE_COLOR = 6 UMETA(DisplayName = "Color"),
EPARMS_TYPE_FLOAT = 10 UMETA(DisplayName = "Float"),
}
void UVariantNode::PinDefaultValueChanged(UEdGraphPin * Pin)
{
if (Pin->PinName == EnumPinName)
{
// 获取当前枚举类型
EPARMS_TYPE Type = (EPARMS_TYPE)EnumType->GetValueByName(FName(*Pin->DefaultValue));
// 找到输出节点
UEdGraphPin* OutputPin = FindPin(OutputPinName);
// 修改输出节点的类型
switch (Type)
{
case EPARMS_TYPE::EPARMS_TYPE_BOOL:
{
OutputPin->PinType.PinSubCategoryObject = nullptr;
OutputPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Boolean;
}
break;
case EPARMS_TYPE::EPARMS_TYPE_COLOR:
{
OutputPin->PinType.PinSubCategoryObject = TBaseStructure<FColor>::Get();
OutputPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Struct;
}
break;
case EPARMS_TYPE::EPARMS_TYPE_FLOAT:
{
OutputPin->PinType.PinSubCategoryObject = nullptr;
OutputPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Float;
}
break;
}
}
}
通过
Pin->PinName可以判断是否是指定的针脚值被修改 根据FindPin(OutputPinName)可以通过名称获得指定的针脚
至于 GetMenuActions 函数就比较套路了,写法比较统一
void UVariantNode::GetMenuActions(FBlueprintActionDatabaseRegistrar & ActionRegistrar) const
{
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
然后就是真正功能实现的地方 ExpandNode 函数
// 绑定的函数的名称
FName FunctionName;
UEnum* CurrentEnumType = FindObject<UEnum>(ANY_PACKAGE, *VariantHelper::EnumName);
EPARMS_TYPE Type = (EPARMS_TYPE)CurrentEnumType->GetValueByName(FName(*EnumPin->DefaultValue));
// 绑定函数
switch (Type)
{
case EPARMS_TYPE::EPARMS_TYPE_BOOL:
FunctionName = GET_FUNCTION_NAME_CHECKED(UVariantHelper, GetBool);
break;
// ....
// ....
// ....
}
// 创建一个函数节点
UK2Node_CallFunction* CallFuncNode = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
CallFuncNode->FunctionReference.SetExternalMember(FunctionName, UVariantHelper::StaticClass());
CallFuncNode->AllocateDefaultPins();
// 执行操作替换
CompilerContext.MovePinLinksToIntermediate(*ExecPin, *(CallFuncNode->GetExecPin()));
CompilerContext.MovePinLinksToIntermediate(*ThenPin, *(CallFuncNode->GetThenPin()));
// 输入和输出替换
FName Name = UEdGraphSchema_K2::PC_String;
CompilerContext.MovePinLinksToIntermediate(*InputPin, *(*CallFuncNode->Pins.FindByPredicate([Name](const UEdGraphPin* EdGraphPin)
{
return EdGraphPin->PinType.PinCategory == Name;
})));
CompilerContext.MovePinLinksToIntermediate(*OutputPin, *(CallFuncNode->GetReturnValuePin()));
// 断开自身所有链接
BreakAllNodeLinks();
上面代码分为三个部分
GET_FUNCTION_NAME_CHECKED(UVariantHelper, GetBool)CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph)CompilerContext.MovePinLinksToIntermediate所以可以这么理解,继承 UK2Node 实现的节点仅用于显示,真正功能实现的是新创建的中间节点 UK2Node_CallFunction,然后将连接在 UK2Node 上的针脚移动到中间节点上,再有中间节点去执行指定的函数,得到最终的结果
| 函数名 | 作用 |
|---|---|
| AllocateDefaultPins() | 为你的节点分配默认的输入和输出引脚 |
| GetMenuActions() | 将你的节点添加到蓝图编辑器的节点菜单中 |
| GetTooltipText() | 提供节点的工具提示文本 |
| GetMenuCategory() | 定义节点在菜单中的分类 |
| GetMenuActions() | 这个函数用于将你的自定义节点添加到蓝图编辑器的节点菜单中 |
| IsNodePure() | 是否是纯函数 |