UE4C++泛型蓝图节点之K2Node蓝图节点

UE4C++泛型蓝图节点之K2Node蓝图节点

  • 前言:

上篇我们讲解了以CustomThunk方式创建自定义泛型节点,今天在此我们讲解以K2Node实现自定义蓝图节点。

首先,我们来比较这俩种方法,CustomThunk创建泛型节点时,代码简洁,调试起来也方便但是灵活度没有K2Node高,K2Node可以动态的改变的节点数量, 是更灵活的动态类型。就如K2Node可以用来创建下拉列表,比如UE源码中UK2Node_GetDataTableRow就是很经典的以K2Node开发的。我们可以看到它这个节点输入的时候是以下拉列表形式,可以选择不同的Datatable表,并输出不同类型的数据结构,这就是K2Node的比较灵活的地方,在更加灵活的开发上,就可以选择以K2Node的方式进行开发。K2Node的存在是用来优化蓝图到C++的远程调用方式(RPC)。
UE4C++泛型蓝图节点之K2Node蓝图节点_第1张图片
K2Node本质上与CustomThunk类型一致,都是以DECLARE_FUNCTION函数的类型进行的,只不过K2Node所对应的DECLARE_FUNCTION比较难找而已。继续拿UK2Node_GetDataTableRow举例,他的DECLARE_FUNCTION函数在UDataTableFunctionLibrary的蓝图函数库里面。
UE4C++泛型蓝图节点之K2Node蓝图节点_第2张图片

在遇到增加删除pin的时候,也会选择以K2Node进行开发实现,例如逻辑操作And节点就能动态的添加删除pin,而此刻用CustomThunk的Thunk函数体实现形式就完全不行,因此,在此我给大家分享一下K2Node的一些功能实现。
UE4C++泛型蓝图节点之K2Node蓝图节点_第3张图片
创建一个类继承自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;

知道这些了,我们再将UK2Node_GetDataTableRow中的代码全拷贝到自己的代码中,例如:

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

#pragma once

#include "CoreMinimal.h"
#include "K2Node.h"
#include "KismetCompiler.h"

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Textures/SlateIcon.h"
#include "EdGraph/EdGraphNodeUtils.h"
#include "Test_K2Node.generated.h"

/**
 *
 */
class FBlueprintActionDatabaseRegistrar;
 class UDataTable;
 class UEdGraph;
UCLASS()
class GAME_SANDBOX_API UTest_K2Node : public UK2Node
{
	GENERATED_BODY()
public:
	/*
	//鼠标放到上面的说明/注释
	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 CreateVisualWidget() { return TSharedPtr(); }
	// 为表示此节点的小部件创建背景图像
	virtual TSharedPtr CreateNodeImage() const { return TSharedPtr(); }
	//右键菜单, 比如添加RemovePin
	virtual void GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override;
	*/
	UTest_K2Node(const FObjectInitializer& ObjectInitializer);
	//~ Begin UEdGraphNode Interface.
	//分配默认引脚
	virtual void AllocateDefaultPins() override;
	//节点名称
	virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
	//引脚更改时会调用
	virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override;
	//鼠标放到上面的说明/注释
	virtual FText GetTooltipText() const override;
	//展开节点
	virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
	virtual FSlateIcon GetIconAndTint(FLinearColor& OutColor) const override;
	virtual void PostReconstructNode() override;
	//~ End UEdGraphNode Interface.

	//~ Begin UK2Node Interface
	virtual bool IsNodeSafeToIgnore() const override { return true; }
	virtual void ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins) override;
	//将节点添加到蓝图视图
	virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
	//蓝图节点的标签
	virtual FText GetMenuCategory() const override;
	virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override;
	virtual void EarlyValidation(class FCompilerResultsLog& MessageLog) const override;
	virtual void PreloadRequiredAssets() override;
	//连接情况更改以后
	virtual void NotifyPinConnectionListChanged(UEdGraphPin* Pin) override;
	//~ End UK2Node Interface

	/** Get the return type of our struct */
	UScriptStruct* GetReturnTypeForStruct();

	/** Get the then output pin */
	UEdGraphPin* GetThenPin() const;
	/** Get the Data Table input pin */
	UEdGraphPin* GetDataTablePin(const TArray<UEdGraphPin*>* InPinsToSearch = NULL) const;
	/** Get the spawn transform input pin */
	UEdGraphPin* GetRowNamePin() const;
	/** Get the exec output pin for when the row was not found */
	UEdGraphPin* GetRowNotFoundPin() const;
	/** Get the result output pin */
	UEdGraphPin* GetResultPin() const;

	/** Get the type of the TableRow to return */
	UScriptStruct* GetDataTableRowStructType() const;

	void OnDataTableRowListChanged(const UDataTable* DataTable);
private:
	/**
	 * Takes the specified "MutatablePin" and sets its 'PinToolTip' field (according
	 * to the specified description)
	 *
	 * @param   MutatablePin	The pin you want to set tool-tip text on
	 * @param   PinDescription	A string describing the pin's purpose
	 */
	void SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const;

	/** Set the return type of our struct */
	void SetReturnTypeForStruct(UScriptStruct* InClass);
	/** Queries for the authoritative return type, then modifies the return pin to match */
	void RefreshOutputPinType();
	/** Triggers a refresh which will update the node's widget; aimed at updating the dropdown menu for the RowName input */
	void RefreshRowNameOptions();

	// 此节点的Tooltip text 
	FText NodeTooltip;

	// 构造FText字符串的开销很大,所以我们缓存节点的标题
	FNodeTextCache CachedNodeTitle;
};
  • .cpp
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "K2Node.h"
#include "KismetCompiler.h"

#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "Textures/SlateIcon.h"
#include "EdGraph/EdGraphNodeUtils.h"
#include "Test_K2Node.generated.h"

/**
 *
 */
class FBlueprintActionDatabaseRegistrar;
 class UDataTable;
 class UEdGraph;
UCLASS()
class GAME_SANDBOX_API UTest_K2Node : public UK2Node
{
	GENERATED_BODY()
public:
	UTest_K2Node(const FObjectInitializer& ObjectInitializer);
	//~ Begin UEdGraphNode Interface.
	//分配默认引脚
	virtual void AllocateDefaultPins() override;
	//节点名称
	virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
	//引脚更改时会调用
	virtual void PinDefaultValueChanged(UEdGraphPin* Pin) override;
	//鼠标放到上面的说明/注释
	virtual FText GetTooltipText() const override;
	//展开节点
	virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
	virtual FSlateIcon GetIconAndTint(FLinearColor& OutColor) const override;
	virtual void PostReconstructNode() override;
	//~ End UEdGraphNode Interface.

	//~ Begin UK2Node Interface
	virtual bool IsNodeSafeToIgnore() const override { return true; }
	virtual void ReallocatePinsDuringReconstruction(TArray<UEdGraphPin*>& OldPins) override;
	//将节点添加到蓝图视图
	virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
	//蓝图节点的标签
	virtual FText GetMenuCategory() const override;
	virtual bool IsConnectionDisallowed(const UEdGraphPin* MyPin, const UEdGraphPin* OtherPin, FString& OutReason) const override;
	virtual void EarlyValidation(class FCompilerResultsLog& MessageLog) const override;
	virtual void PreloadRequiredAssets() override;
	//连接情况更改以后
	virtual void NotifyPinConnectionListChanged(UEdGraphPin* Pin) override;
	//~ End UK2Node Interface

	/** Get the return type of our struct */
	UScriptStruct* GetReturnTypeForStruct();

	/** Get the then output pin */
	UEdGraphPin* GetThenPin() const;
	/** Get the Data Table input pin */
	UEdGraphPin* GetDataTablePin(const TArray<UEdGraphPin*>* InPinsToSearch = NULL) const;
	/** Get the spawn transform input pin */
	UEdGraphPin* GetRowNamePin() const;
	/** Get the exec output pin for when the row was not found */
	UEdGraphPin* GetRowNotFoundPin() const;
	/** Get the result output pin */
	UEdGraphPin* GetResultPin() const;

	/** Get the type of the TableRow to return */
	UScriptStruct* GetDataTableRowStructType() const;

	void OnDataTableRowListChanged(const UDataTable* DataTable);
private:
	/**
	 * Takes the specified "MutatablePin" and sets its 'PinToolTip' field (according
	 * to the specified description)
	 *
	 * @param   MutatablePin	The pin you want to set tool-tip text on
	 * @param   PinDescription	A string describing the pin's purpose
	 */
	void SetPinToolTip(UEdGraphPin& MutatablePin, const FText& PinDescription) const;

	/** Set the return type of our struct */
	void SetReturnTypeForStruct(UScriptStruct* InClass);
	/** Queries for the authoritative return type, then modifies the return pin to match */
	void RefreshOutputPinType();
	/** Triggers a refresh which will update the node's widget; aimed at updating the dropdown menu for the RowName input */
	void RefreshRowNameOptions();

	// 此节点的Tooltip text 
	FText NodeTooltip;

	// 构造FText字符串的开销很大,所以我们缓存节点的标题
	FNodeTextCache CachedNodeTitle;
};

如果代码改的发现还有错误,这时候添加模块,此处我选择偷懒,是将UK2Node_GetDataTableRow所在的模块添加进入.build.cs文件中,在将其中的private中的文件拷贝到.build.cs中,因为,私有模块在添加之后,其他模块是无法使用的。我之前的模块添加应该有讲到,感兴趣的可以稍微看看。

UE4C++泛型蓝图节点之K2Node蓝图节点_第4张图片
以下便是添加好的模块

PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "BlueprintGraph" });

        PrivateDependencyModuleNames.AddRange(
            new string[]
            {
                "Slate",
                "SlateCore", 
                "ApplicationCore",
                "EditorStyle",
                "KismetCompiler",
                "UnrealEd",
                "GraphEditor",
                "Kismet",
                "KismetWidgets",
                "PropertyEditor",
                "ToolMenus",
            });

运行之后选择Datatable和不选择Datatable便是这俩种情况
UE4C++泛型蓝图节点之K2Node蓝图节点_第5张图片
UE4C++泛型蓝图节点之K2Node蓝图节点_第6张图片
这时候我们再深入研究代码即可

  • 创建输入引脚
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
  • 创建输出引脚
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);

数组类型的输出引脚

// Create the output pin
UEdGraphNode::FCreatePinParams PinParams;
PinParams.ContainerType = EPinContainerType::Array;
PinParams.bIsReference = false;
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Byte, UEdGraphSchema_K2::PN_ReturnValue, PinParams);

继承 IK2Node_AddPinInterface接口后重写virtual void AddInputPin() override函数添加Pin

void UK2Node_ConvertScriptCallValue::AddInputPin()
{
	CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, GetPinName(NumInputs));
	NumInputs++;
}

添加移除Pin的操作

void UK2Node_ConvertScriptCallValue::GetNodeContextMenuActions(UToolMenu* Menu, UGraphNodeContextMenuContext* Context) const
{
	Super::GetNodeContextMenuActions(Menu, Context);
 
	if (!Context->bIsDebugging)
	{
		static FName CommutativeAssociativeBinaryOperatorNodeName = FName("K2Node_ConvertScriptCallValue");
		FText CommutativeAssociativeBinaryOperatorStr = LOCTEXT("K2Node_ConvertScriptCallValue", "Operator Node");
		if (Context->Pin != NULL)
		{
			if (CanRemovePin(Context->Pin))
			{
				FToolMenuSection& Section =
					Menu->AddSection(CommutativeAssociativeBinaryOperatorNodeName, CommutativeAssociativeBinaryOperatorStr);
				Section.AddMenuEntry("RemovePin", LOCTEXT("RemovePin", "Remove pin"),
					LOCTEXT("RemovePinTooltip", "Remove this input pin"), FSlateIcon(),
					FUIAction(FExecuteAction::CreateUObject(const_cast<UK2Node_ConvertScriptCallValue*>(this),
						&UK2Node_ConvertScriptCallValue::RemoveInputPin, const_cast<UEdGraphPin*>(Context->Pin))));
 
			}
		}
	}
}

移除pin

void UK2Node_ConvertScriptCallValue::RemoveInputPin(UEdGraphPin * Pin)
{
	FScopedTransaction Transaction(FText::FromString("ConvertScriptCallValue_RemoveInputPin"));
	Modify();
	--NumInputs;
	RemovePin(Pin);
	SyncPinNames();
	FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}

次篇幅就到此为止,下章讲解详细的使用,大家喜欢就点个赞吧!

你可能感兴趣的:(UEC++,ue4,c++)