Published on

K2Node深入探索及蓝图泛型节点

前言

本文记录一下蓝图泛型节点的实现, 主要分为CustomThunk方式和K2Node的方式

对比一下两者的优点

CustomThunk: 代码少, 调试方便

UK2Node: 动态的节点数量, 更灵活的动态类型

UE4Editor_OO8h1IPyH8

本文代码在如下项目中

BlueprintLibraryUtility

CustomThunk

此函数分为3部分

  1. 带有UFUNCTION宏的函数声明;
  2. 自定义Thunk函数体DECLARE_FUNCTION(execFunctionName)
  3. 真正执行泛型逻辑的Generic_FunctionName()泛型函数。

函数声明

  1. 需要加入字段CustomThunk
  2. meta数据内指定哪些参数为泛型类型,如普通便变量的meta = (CustomStructureParam = "Value1,Value2,Value")

特殊情况

  • 普通变量

CustomStructureParam = "变量1,变量2"的方式

只能用符号","

  • TArray

meta = (ArrayParm = "Array1,Array2,Array3", ArrayTypeDependentParams = "Array1,Array2,Array3")

ArrayTypeDependentParams表示多个类型的相互依赖, 参考引擎文件KismetArrayLibrary.h

  • TMap

meta = (MapParam = "TargetMap", MapKeyParam = "Key", MapValueParam = "Value")

Map只能指定一个TMap类型的泛型类型,多个泛型无法正常工作

  • TSet

meta = (SetParam = "Set1|Set2,Set3")

TSet比较特殊, 用","分隔多个泛型, 用"|"表示类型依赖

实现

DECLARE_FUNCTION来手动定义函数实现(取代的*.\Intermediate\Build\Win64\UE4Editor\Inc**.gen.cpp内的代码), 一般需要声明前缀为Generic的同名函数(参数可以不同)

如我们实现一个结构体转Json的函数

	UFUNCTION(BlueprintPure, CustomThunk, meta = (CustomStructureParam = "StructReference", DisplayName = "Struct to JSON String"), Category = "File|Json")
		static void UStructToJsonObjectString(const int32& StructReference, FString& JSONString);
	static void GenericUStructToJsonObjectString(const UStruct* StructDefinition, const void* Struct, FString& OutJsonString, int64 CheckFlags, 
	int64 SkipFlags);

	DECLARE_FUNCTION(execUStructToJsonObjectString)
	{
		
		Stack.`StepCompiledIn<FStructProperty>`(NULL);
		FStructProperty* StructProperty = `CastField<FStructProperty>`(Stack.MostRecentProperty);
		void* StructPtr = Stack.MostRecentPropertyAddress;

		P_GET_PROPERTY_REF(FStrProperty, JSONString);

		P_FINISH;

		P_NATIVE_BEGIN;

		GenericUStructToJsonObjectString(StructProperty->Struct,StructPtr,JSONString,0,0);

		P_NATIVE_END;
	}

P_FINISH;之前是获取属性

获取属性的先后次序应与函数声明时变量在参数列表中出现的次序保持一致

P_NATIVE_BEGIN;P_NATIVE_END;之间是定义函数的实现

获取属性的方式

  • 普通变量
Stack.`StepCompiledIn<UStructProperty>`(NULL);
void* SrcPropertyAddr = Stack.MostRecentPropertyAddress;
UProperty* SrcProperty = `Cast<UProperty>`(Stack.MostRecentProperty);
  • Array
  Stack.`StepCompiledIn<UArrayProperty>`(NULL);
  void* SrcArrayAddr = Stack.MostRecentPropertyAddress;
  UArrayProperty* SrcArrayProperty = `Cast<UArrayProperty>`(Stack.MostRecentProperty);
  • Map
  Stack.MostRecentProperty = nullptr;
  Stack.`StepCompiledIn<UMapProperty>`(NULL);
  void* SrcMapAddr = Stack.MostRecentPropertyAddress;
  UMapProperty* SrcMapProperty = `Cast<UMapProperty>`(Stack.MostRecentProperty);
  • Set
  Stack.MostRecentProperty = nullptr;
  Stack.`StepCompiledIn<USetProperty>`(NULL);
  void* SetAddr = Stack.MostRecentPropertyAddress;
  USetProperty* SetProperty = `Cast<USetProperty>`(Stack.MostRecentProperty);

K2Node

继承于UK2Node

一般会重写如下几个函数

//鼠标放到上面的说明/注释
virtual FText GetTooltipText()const override;
//节点名称
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType)const override;
//将节点添加到蓝图视图
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegister)const override;
//蓝图节点的标签
virtual FText GetMenuCategory()const ;
//展开节点
virtual void ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)override ;
//分配默认引脚
virtual void AllocateDefaultPins()override;
//引脚更改时会调用
virtual void PinDefaultValueChanged(UEdGraphPin* ChangedPin)override;
//连接情况更改以后
virtual void NotifyPinConnectionListChanged(UEdGraphPin* Pin)override;

还有一些特殊情况会需要重写的函数, 比如

/** 创建一个可视小部件来在图形编辑器或图形面板中表示这个节点。如果没有实现,则将使用默认的节点工厂 */
	virtual `TSharedPtr<SGraphNode>` CreateVisualWidget() { return `TSharedPtr<SGraphNode>`(); }
/** 为表示此节点的小部件创建背景图像 */
	virtual `TSharedPtr<SWidget>` CreateNodeImage() const { return `TSharedPtr<SWidget>`(); }
//右键菜单, 比如添加RemovePin
virtual void GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override;

更多请参考 EdGraphNode.hK2Node.h

下面我们创建几个K2Node

K2Node_Print

UE4Editor_6iO5ybXnXh

由于平时用蓝图的PrintString打印字符串经常需要输入Append,不方便, 那么我们就借此尝试做一个K2Node版本的可以动态添加引脚的Print

直接上代码,重点加注释

//.h
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "K2Node.h"
#include "K2Node_CallFunction.h"
#include "KismetCompiler.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "K2Node_Print.generated.h"

/**
 * 
 */
UCLASS()
class BLUEPRINTLIBRARYUTILITYEDITOR_API UK2Node_Print : public UK2Node
{
	GENERATED_BODY()

public:
	virtual FText GetTooltipText()const override
	{
		return FText::FromString("Printf");
	}

	virtual FText GetNodeTitle(ENodeTitleType::Type TitleType)const override
	{
		return FText::FromString(TEXT("Printf"));
	}
	virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegister)const override
	{
		UClass* ActionKey = GetClass();
		if (ActionRegister.IsOpenForRegistration(ActionKey))
		{
			UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
			check(NodeSpawner);
			ActionRegister.AddBlueprintAction(ActionKey, NodeSpawner);
		}
	}

	virtual void GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override;

	virtual FText GetMenuCategory()const {
		return FText::FromString(TEXT("BlueprintLibraryUtility|IO"));
	}
	UEdGraphPin* GetThenPin() const
	{
		UEdGraphPin* Pin = FindPin(UEdGraphSchema_K2::PN_Then);
		check(Pin == nullptr || Pin->Direction == EGPD_Output); // If pin exists, it must be output
		return Pin;
	}
	

	virtual void ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)override;
	virtual void AllocateDefaultPins()override;
	virtual void PinDefaultValueChanged(UEdGraphPin* ChangedPin)override;



	void AddPinToNode();
	void RemoveInputPin(UEdGraphPin* Pin);

	UFUNCTION(BlueprintCallable, meta = ( CallableWithoutWorldContext = true, WorldContext = "context"))


	virtual `TSharedPtr<SGraphNode>` CreateVisualWidget() override;

private:
	UPROPERTY()
		`TArray<FName>` ArgPinNames;

		static FName PN_PrintToSreen;
		static FName PN_PrintToLog;
		static FName PN_Color;
		static FName PN_Duration;
};

//cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "Node/K2Node_Print.h"

#include "Kismet/KismetStringLibrary.h"
#include "Kismet/KismetSystemLibrary.h"
#include "K2Node_MakeArray.h"
#include "Node/GraphNode_Printf.h"
#include "ToolMenu.h"
#include "ToolMenuSection.h"


#define LOCTEXT_NAMESPACE "K2Node_Print"

FName UK2Node_Print::PN_Color = TEXT("Color");
FName UK2Node_Print::PN_PrintToLog = TEXT("PrintToLog");
FName UK2Node_Print::PN_PrintToSreen = TEXT("PrintToScreen");
FName UK2Node_Print::PN_Duration = TEXT("Duration");



void UK2Node_Print::GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const
{
	Super::GetNodeContextMenuActions(Menu,Context);

	if (!Context->bIsDebugging)
	{
		if (Context->Pin != FindPin(UK2Node_Print::PN_PrintToSreen) && 
			Context->Pin != FindPin(UK2Node_Print::PN_Color) &&
			Context->Pin != FindPin(UK2Node_Print::PN_PrintToLog) &&
			Context->Pin != FindPin(UK2Node_Print::PN_Duration))
		{
            //添加移除节点的右键功能,否则无法移除变量会很尴尬
			FToolMenuSection& Section = Menu->AddSection(FName(TEXT("UK2Node_Print")), LOCTEXT("UK2Node_Print","Action"));
			Section.AddMenuEntry(
				"RemovePin",
				LOCTEXT("RemovePin", "Remove pin"),
				LOCTEXT("RemovePinTooltip", "Remove this input pin"),
				FSlateIcon(),
				FUIAction(
					FExecuteAction::CreateUObject(const_cast<UK2Node_Print*>(this), &UK2Node_Print::RemoveInputPin, const_cast<UEdGraphPin*>(Context->Pin))
				)
			);
		}
	}

}



void UK2Node_Print::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	UEdGraphPin* ExecPin = GetExecPin();
	UEdGraphPin* ThenPin = GetThenPin();
	if (ExecPin && ThenPin) 
	{
        //获取函数库的函数, 不能使用自己的函数,否则非编辑器模式运行崩溃
		UFunction* Function = UFlib_IO::StaticClass()->FindFunctionByName(FName("PrintArray"));
		if (Function == NULL)
		{
			CompilerContext.MessageLog.Error(*LOCTEXT("InvalidFunctionName", "BaseAsyncTask: Type not supported or not initialized. @@").ToString(), this);
			return;
		}

		UK2Node_CallFunction* CallFuncNode = CompilerContext.`SpawnIntermediateNode<UK2Node_CallFunction>`(this, SourceGraph);

		CallFuncNode->SetFromFunction(Function);
		CallFuncNode->AllocateDefaultPins();
		CompilerContext.MovePinLinksToIntermediate(*ExecPin, *(CallFuncNode->GetExecPin()));
		CompilerContext.MovePinLinksToIntermediate(*ThenPin, *(CallFuncNode->GetThenPin()));

		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_Print::PN_PrintToSreen), *CallFuncNode->FindPin(TEXT("bScreen")));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_Print::PN_PrintToLog), *CallFuncNode->FindPin(TEXT("bLog")));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_Print::PN_Color), *CallFuncNode->FindPin(TEXT("Color")));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_Print::PN_Duration), *CallFuncNode->FindPin(TEXT("Duration")));

//如下添加一个特殊的节点, 用于动态添加Pin
		UK2Node_MakeArray* MakeArrayNode = CompilerContext.`SpawnIntermediateNode<UK2Node_MakeArray>`(this, SourceGraph);
		MakeArrayNode->AllocateDefaultPins();

		UEdGraphPin* ArrayOut = MakeArrayNode->GetOutputPin();
		UEdGraphPin* FuncArgPin = CallFuncNode->FindPinChecked(TEXT("InStrings"));
		ArrayOut->MakeLinkTo(FuncArgPin);

		MakeArrayNode->PinConnectionListChanged(ArrayOut);
		// connect all arg pin to Make Array input
		for (int32 i = 0; i < ArgPinNames.Num(); i++) {

			// Make Array node has one input by default
			if (i > 0)
				MakeArrayNode->AddInputPin();

			// find the input pin on the "Make Array" node by index.
			const FString PinName = FString::Printf(TEXT("[%d]"), i);
			UEdGraphPin* ArrayInputPin = MakeArrayNode->FindPinChecked(PinName);

			// move input word to array 
			UEdGraphPin* MyInputPin = FindPinChecked(ArgPinNames[i], EGPD_Input);
			CompilerContext.MovePinLinksToIntermediate(*MyInputPin, *ArrayInputPin);
		}// end of for
	}

	BreakAllNodeLinks();
}

void UK2Node_Print::AllocateDefaultPins()
{
	Super::AllocateDefaultPins();
    //创建默认节点, 同时指定默认值
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
	CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
	UEdGraphPin* PinScreen = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, UK2Node_Print::PN_PrintToSreen);
	PinScreen->DefaultValue = "True";
	UEdGraphPin* PinLog = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, UK2Node_Print::PN_PrintToLog);
	PinLog->DefaultValue = "True";
	UScriptStruct* ColorStruct = `TBaseStructure<FLinearColor>`::Get();
	UEdGraphPin* PinColor = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Struct, ColorStruct, UK2Node_Print::PN_Color);
	FLinearColor color = FLinearColor::Green;
	PinColor->DefaultValue = color.ToString();

	UEdGraphPin* PinDuration = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Float, UK2Node_Print::PN_Duration);
	PinDuration->DefaultValue = "2.0";

	for (const FName& PinName : ArgPinNames) {
		CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, PinName);
	}
}

void UK2Node_Print::PinDefaultValueChanged(UEdGraphPin* ChangedPin)
{
	Super::PinDefaultValueChanged(ChangedPin);
}


void UK2Node_Print::AddPinToNode()
{
	Modify();

	TMap<FString, FStringFormatArg> FormatArgs = {
			{TEXT("Count"), ArgPinNames.Num()}
	};

	FName NewPinName(*FString::Format(TEXT("{Count}"), FormatArgs));
	ArgPinNames.Add(NewPinName);

	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_String, NewPinName);
}

void UK2Node_Print::RemoveInputPin(UEdGraphPin* Pin)
{
	FScopedTransaction Transaction(FText::FromString("Printf_RemoveInputPin"));
	Modify();

	ArgPinNames.Remove(Pin->GetFName());

	RemovePin(Pin);
	FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}









`TSharedPtr<SGraphNode>` UK2Node_Print::CreateVisualWidget()
{
	return SNew(SGraphNode_Printf, this);
}

#undef LOCTEXT_NAMESPACE
  • 函数库
void UFlib_IO::PrintArray(UObject* context,  const `TArray<FString>`& InStrings, bool bScreen, bool bLog, FLinearColor Color, float Duration)
{
	FString OutString(TEXT(""));
	for (const auto& Word : InStrings)
		OutString += Word;
	UKismetSystemLibrary::PrintString(context,OutString,bScreen,bLog,Color,Duration);
}

辅助类

我们还需要一个继承于SGraphNodeK2Base的辅助类

//.h


#pragma once

#include "CoreMinimal.h"
#include "KismetNodes/SGraphNodeK2Base.h"
//#include "GraphNode_Printf.generated.h"

class UK2Node_Print;
class SVerticalBox;

/**
 * 
 */
class BLUEPRINTLIBRARYUTILITYEDITOR_API SGraphNode_Printf : public SGraphNodeK2Base
{
public:
	SLATE_BEGIN_ARGS(SGraphNode_Printf) {}
	SLATE_END_ARGS()

		void Construct(const FArguments& InArgs, UEdGraphNode* InNode);
protected:
	virtual void CreateInputSideAddButton(`TSharedPtr<SVerticalBox>` InputBox) override;
	virtual FReply OnAddPin() override;
};

//.cpp
// Fill out your copyright notice in the Description page of Project Settings.


#include "Node/GraphNode_Printf.h"
#include "GraphEditorSettings.h"
#include "SGraphNode.h"
#include "Node/K2Node_Print.h"



void SGraphNode_Printf::Construct(const FArguments& InArgs, UEdGraphNode* InNode)
{
	this->GraphNode = InNode;

	this->SetCursor(EMouseCursor::CardinalCross);

	this->UpdateGraphNode();
}

void SGraphNode_Printf::CreateInputSideAddButton(`TSharedPtr<SVerticalBox>` InputBox)
{
	FText AddPinName = FText::FromString(TEXT("Add Pin"));
	FText RemovePinName = FText::FromString(TEXT("Remove Pin"));
	`TSharedRef<SWidget>` AddPinButton = AddPinButtonContent(AddPinName, AddPinName);
	`TSharedRef<SWidget>` RemovePinButton = AddPinButtonContent(RemovePinName, RemovePinName);


	FMargin AddPinPadding = Settings->GetInputPinPadding();
	AddPinPadding.Top += 6.0f;

	InputBox->AddSlot()
		.AutoHeight()
		.VAlign(VAlign_Center)
		.Padding(AddPinPadding)
		[
			AddPinButton
		];
}

FReply SGraphNode_Printf::OnAddPin()
{
	UK2Node_Print* BPNode = `CastChecked<UK2Node_Print>`(GraphNode);

	const FScopedTransaction Transaction(NSLOCTEXT("Kismet", "AddArgumentPin", "Add Argument Pin"));
	BPNode->Modify();

	BPNode->AddPinToNode();
	FBlueprintEditorUtils::MarkBlueprintAsModified(BPNode->GetBlueprint());

	UpdateGraphNode();
	GraphNode->GetGraph()->NotifyGraphChanged();

	return FReply::Handled();
}


K2Node_GetMatProperty

录制_2021_03_22_10_06_11_380

这个是基于assimp开源库制作的从外部文件获取模型材质球的节点, 关于assimp库的内容参考 开源图形库Assimp

此节点特殊点有几个

  • 输入参数根据枚举的变化, 会动态增加/删除输入引脚
  • 返回参数类型是动态的,根据输入枚举
void UK2Node_GetMatProperty::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
	Super::ExpandNode(CompilerContext, SourceGraph);

	UEdGraphPin* ExecPin = GetExecPin();
	UEdGraphPin* ThenPin = GetThenPin();
	if (ExecPin && ThenPin) {

		UFunction* Function = UFlib_IO::StaticClass()->FindFunctionByName(FunctionName);
		if (Function == NULL)
		{
			CompilerContext.MessageLog.Error(*LOCTEXT("InvalidFunctionName", "BaseAsyncTask: Type not supported or not initialized. @@").ToString(), this);
			return;
		}

		UK2Node_CallFunction* CallFuncNode = CompilerContext.`SpawnIntermediateNode<UK2Node_CallFunction>`(this, SourceGraph);

		CallFuncNode->SetFromFunction(Function);
		CallFuncNode->AllocateDefaultPins();

		// move pins
		CompilerContext.MovePinLinksToIntermediate(*ExecPin, *(CallFuncNode->GetExecPin()));
		CompilerContext.MovePinLinksToIntermediate(*ThenPin, *(CallFuncNode->GetThenPin()));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_GetMatProperty::PinName_File), *(CallFuncNode->FindPin(UK2Node_GetMatProperty::PinName_File)));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_GetMatProperty::PinName_Key), *(CallFuncNode->FindPin(UK2Node_GetMatProperty::PinName_Key)));
		CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_GetMatProperty::PinName_Return), *(CallFuncNode->FindPin(UK2Node_GetMatProperty::PinName_Return)));
		UEdGraphPin* tPin = FindPin(UK2Node_GetMatProperty::PinName_T);
		//这里对2个扩展Pin选择是否连接
        if (tPin && !tPin->bHidden)
		{
			CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_GetMatProperty::PinName_T), *(CallFuncNode->FindPin(UK2Node_GetMatProperty::PinName_T)));
		}
		UEdGraphPin* nPin = FindPin(UK2Node_GetMatProperty::PinName_N);
		if (nPin && !nPin->bHidden)
		{
			CompilerContext.MovePinLinksToIntermediate(*FindPin(UK2Node_GetMatProperty::PinName_N), *(CallFuncNode->FindPin(UK2Node_GetMatProperty::PinName_N)));
		}
		
	}

	// break any links to the expanded node
	BreakAllNodeLinks();
}

void UK2Node_GetMatProperty::RefreshExtraPin(EMatPropertyKeyType key)
{
	bool bHasExtraPin =  key == EMatPropertyKeyType::TEXTURE || key == EMatPropertyKeyType::TEXBLEND || key == EMatPropertyKeyType::TEXOP ||
		key == EMatPropertyKeyType::MAPPING || key == EMatPropertyKeyType::UVWSRC || key == EMatPropertyKeyType::MAPPINGMODE_U || 
		key == EMatPropertyKeyType::MAPPINGMODE_V || key == EMatPropertyKeyType::TEXMAP_AXIS || key == EMatPropertyKeyType::TEXFLAGS;

		UEdGraphPin* typePin = FindPin(UK2Node_GetMatProperty::PinName_T);
		UEdGraphPin* nPin = FindPin(UK2Node_GetMatProperty::PinName_N);
		if (typePin)
		{
			typePin->bHidden = !bHasExtraPin;
			if (!bHasExtraPin)
			{
				typePin->BreakAllPinLinks();
			}
		}
		if (nPin)
		{
			nPin->bHidden = !bHasExtraPin;
			if (!bHasExtraPin)
			{
				nPin->BreakAllPinLinks();
			}
		}	
}

上面函数根据枚举类型来选择是否隐藏/显示对应的Pin

void UK2Node_GetMatProperty::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
    ...................
	UFunction* Function = UFlib_IO::StaticClass()->FindFunctionByName(FunctionName);
    ...................
}
void UK2Node_GetMatProperty::RefreshReturnPin(EReturnType newType)
{
	UEdGraph* Graph = GetGraph();
	if (Graph)
	{
		UEdGraphPin* oldRetPin = FindPin(UK2Node_GetMatProperty::PinName_Return);
		if (oldRetPin)
		{
			RemovePin(oldRetPin);
			Graph->NotifyGraphChanged();
		}
		Graph->NotifyGraphChanged();
	}
	
	

	switch (newType)
	{
	case EReturnType::Color:
	{
		FunctionName = GetFunctionName(EReturnType::Color);
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Struct, ColorStruct, UK2Node_GetMatProperty::PinName_Return);
		break;
	}
	case EReturnType::Vector:
	{
		FunctionName = GetFunctionName(EReturnType::Vector);
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Struct, VectorStruct, UK2Node_GetMatProperty::PinName_Return);
		break;
	}
	case EReturnType::Int:
	{
		FunctionName = GetFunctionName(EReturnType::Int);
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Int, UK2Node_GetMatProperty::PinName_Return);
		break;
	}
	case EReturnType::Float:
	{
		FunctionName = GetFunctionName(EReturnType::Float);
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Float, UK2Node_GetMatProperty::PinName_Return);
		break;
	}
	case EReturnType::String:
	{
		FunctionName = GetFunctionName(EReturnType::String);
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_String, UK2Node_GetMatProperty::PinName_Return);
		break;
	}
	default:
		FunctionName = GetFunctionName(EReturnType::String);
		CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_String, UK2Node_GetMatProperty::PinName_Return);
		break;
	}
}

根据类型设置不同的FunctionName然后来获取函数指针

K2Node的泛型

录制_2021_03_25_10_51_28_192

我们顺路在上面节点加入一个泛型的输入变量

void UK2Node_GetMatProperty::AllocateDefaultPins()
{
    ..............
UEdGraphPin* testPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, TEXT("TEST"));
}

然后重写virtual void PinTypeChanged(UEdGraphPin* Pin) override;

void UK2Node_GetMatProperty::PinTypeChanged(UEdGraphPin* Pin)
{
	
	if (Pin->PinName == TEXT("TEST"))
	{
		UEdGraphPin* testPin = FindPin(TEXT("TEST"));
		if (Pin->LinkedTo.Num()>0)
		{
			UEdGraphPin* InstigatorPin = Pin->LinkedTo[0];
			if (testPin->PinType != InstigatorPin->PinType)
			{
				testPin->PinType = InstigatorPin->PinType;
			}	
		}
		else
		{
			testPin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;
			testPin->PinType.PinSubCategory = NAME_None;
			testPin->PinType.PinSubCategoryObject = nullptr;
		}

		GetGraph()->NotifyGraphChanged();

		UBlueprint* Blueprint = GetBlueprint();
		if (!Blueprint->bBeingCompiled)
		{
			FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint);
			Blueprint->BroadcastChanged();
		}
	}

	Super::PinTypeChanged(Pin);
}

然后在Pin链接改变以后就调用

void UK2Node_GetMatProperty::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{
	Super::NotifyPinConnectionListChanged(Pin);
	PinTypeChanged(Pin);
}