这篇主要以一个蓝图节点的示例来说明大概怎么写一个自定义蓝图节点。
这个自定义K2Node蓝图节点其实是为了优化是“C++利用反射UFUNCTION实现RPC”的。之前调用服务器函数是使用泛型蓝图节点然后解析里面的FPerperty类型序列化后传给服务器。但是这有个问题就是函数的参数数量和类型是变化的,之前是定义了0-8参数的各个泛型函数和一个以结构体包含的(结构体里包含了所有参数)泛型函数来实现的,这个实现是能够符合需求的,但是不方便。现在通过这个蓝图节点实现。
我先放上解释,最后有.h和.cpp代码。
一、
函数解析:
1.
void UK2Node_ConvertScriptCallValue::AllocateDefaultPins()
{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 pinUEdGraphNode::FCreatePinParams PinParams;PinParams.ContainerType = EPinContainerType::Array;PinParams.bIsReference = false;CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Byte, UEdGraphSchema_K2::PN_ReturnValue, PinParams);for (int32 i = 0; i < NumInputs; ++i){CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, GetPinName(i));}
}
创建Pin函数:
1)创建一个输入引脚
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
2)创建一个输出引脚
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
3)一个TArray<Byte>类型的输出Pin
// Create the output pinUEdGraphNode::FCreatePinParams PinParams;PinParams.ContainerType = EPinContainerType::Array;PinParams.bIsReference = false;CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Byte, UEdGraphSchema_K2::PN_ReturnValue, PinParams);
4)根据NumInput的值初始化输入Pin,NumInputs是输入Pin计数器,在添加删除时计数,序列化保存着。注意创建的是PC_Wildcard泛型类型,因为一开始是不确定类型的。
for (int32 i = 0; i < NumInputs; ++i){CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, GetPinName(i));}
2.
void UK2Node_ConvertScriptCallValue::PostReconstructNode()
{TArray<UEdGraphPin*> ValuesPins = GetValuePin();for (int i = 0; i < ValuesPins.Num(); i++){UEdGraphPin* Pin = ValuesPins[i];NotifyPinConnectionListChanged(Pin);}
}
这个是ReconstructNode()函数里会调用的函数,Node Spawn的时候会调用一次ReconstructNode(),这个函数会调用到,我在这里设置Pin的类型。ValuesPins是除了输入引脚外的输入Pin。然后调用NotifyPinConnectionListChanged(Pin);设置Pin。
3.
void UK2Node_ConvertScriptCallValue::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{Super::NotifyPinConnectionListChanged(Pin);if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec || Pin->Direction == EGPD_Output)return;// If the pin is linked, make sure the other wildcard pins matchif (Pin->LinkedTo.Num() > 0){UEdGraphPin* LinkPin = Pin->LinkedTo[0];if (Pin->PinType != LinkPin->PinType){Pin->PinType = MoveTemp(LinkPin->PinType);GetGraph()->NotifyGraphChanged();}}else if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard){Pin->PinType.ResetToDefaults();Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;GetGraph()->NotifyGraphChanged();}
}
NotifyPinConnectionListChanged是当有其他Pin连线到该Node里的Pin或断开该Node的Pin时会调用的函数。在这个函数里设置Pin的值,在有连线的情况即Pin->LinkedTo.Num()下把当前的Pin的类型改成连线Pin的类型,在没有连线的Pin的情况下设置为PC_Wildcard
4.
继承 IK2Node_AddPinInterface接口后重写virtual void AddInputPin() override函数添加Pin
void UK2Node_ConvertScriptCallValue::AddInputPin()
{CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, GetPinName(NumInputs));NumInputs++;
}
添加一个PC_Wildcard类型的Pin并添加计数
5.
添加移除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))));}}}
}
这个是Node的MenuAction,对Node右键点击会调用。Context->Pin!=NULL即当右键Pin时添加移除Action,移除时调用RemoveInputPin函数。
6.
移除Pin
void UK2Node_ConvertScriptCallValue::RemoveInputPin(UEdGraphPin * Pin)
{FScopedTransaction Transaction(FText::FromString("ConvertScriptCallValue_RemoveInputPin"));Modify();--NumInputs;RemovePin(Pin);SyncPinNames();FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}
7.
关于节点的Pin的操作上面已经都有了,现在就是这个Node具体要干什么了。这个Node本身的目的是能够处理可变数量的泛型参数的处理。可变数量一般都会想到数组,但我们的参数是不同的类型,所以数组是不可行的。没有容器,基本上不能用一个函数来处理。所以这个Node会调用多个函数。使用FNodeHandlingFunctor派生类。
?
FNodeHandlingFunctor* UK2Node_ConvertScriptCallValue::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{return new FKCHandler_ConvertScriptCallValue(CompilerContext);
}?
把Node与FNodeHandlingFunctor派生类关联。
二、FNodeHandlingFunctor派生类
class FKCHandler_ConvertScriptCallValue : public FNodeHandlingFunctor
{
protected:FBPTerminal* ObjectTerm = nullptr;public:FKCHandler_ConvertScriptCallValue(FKismetCompilerContext& InCompilerContext) : FNodeHandlingFunctor(InCompilerContext){}virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override{UK2Node_ConvertScriptCallValue* MyNode = CastChecked<UK2Node_ConvertScriptCallValue>(Node);{UEdGraphPin* ReturnPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);UEdGraphPin* ReturnPinToTry = FEdGraphUtilities::GetNetFromPin(ReturnPin);FBPTerminal* Term =Context.CreateLocalTerminalFromPinAutoChooseScope(ReturnPin, Context.NetNameMap->MakeValidName(ReturnPin));Term->bPassedByReference = false;Term->Source = Node;Context.NetMap.Add(ReturnPin, Term);}ObjectTerm = Context.CreateLocalTerminal();ObjectTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Object;ObjectTerm->Source = Node;ObjectTerm->Name = Context.NetNameMap->MakeValidName(Node) + TEXT("_TempObject");FNodeHandlingFunctor::RegisterNets(Context, Node);}virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override{UK2Node_ConvertScriptCallValue* MyNode = CastChecked<UK2Node_ConvertScriptCallValue>(Node);UEdGraphPin* ReturnPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);FBPTerminal* ReturnValue = nullptr;FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin);if (ReturnTerm != nullptr && *ReturnTerm != nullptr)ReturnValue = *ReturnTerm;TArray<UEdGraphPin*> ValuesPins = MyNode->GetValuePin();TArray<FBPTerminal*> InputTermArray;for (int i = 0; i < ValuesPins.Num(); i++){UEdGraphPin* InputPin = ValuesPins[i];if(!InputPin)continue;UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(InputPin);FBPTerminal** pInputTerm = Context.NetMap.Find(PinToTry);if (pInputTerm != nullptr && *pInputTerm != nullptr)InputTermArray.Add(*pInputTerm);}UClass* ParseValueClass = UScriptCallParseValue::StaticClass();UFunction* CreateFuncPtr = FindUField<UFunction>(ParseValueClass, "CreatePraseValueObject");UFunction* AddPrefixFuncPtr = FindUField<UFunction>(ParseValueClass, "AddPrefixToArchive");UFunction* AddSuffixFuncPtr = FindUField<UFunction>(ParseValueClass, "AddSuffixToArchive");UFunction* ObjectParseValue2BufferFuncPtr = FindUField<UFunction>(ParseValueClass, "ObjectParseValue2Buffer");UFunction* ToByteArrayFuncPtr = FindUField<UFunction>(ParseValueClass, "ArchiveToBuffer");FBlueprintCompiledStatement& CallCreateFuncPtr = Context.AppendStatementForNode(MyNode);CallCreateFuncPtr.Type = KCST_CallFunction;CallCreateFuncPtr.FunctionToCall = CreateFuncPtr;CallCreateFuncPtr.LHS = ObjectTerm;FBlueprintCompiledStatement& CallAddPrefixFuncPtr = Context.AppendStatementForNode(MyNode);CallAddPrefixFuncPtr.Type = KCST_CallFunction;CallAddPrefixFuncPtr.FunctionToCall = AddPrefixFuncPtr;CallAddPrefixFuncPtr.RHS.Add(ObjectTerm);for (int i = 0; i < InputTermArray.Num(); i++){FBlueprintCompiledStatement& CallParseStructFuncPtr = Context.AppendStatementForNode(MyNode);CallParseStructFuncPtr.Type = KCST_CallFunction;CallParseStructFuncPtr.FunctionToCall = ObjectParseValue2BufferFuncPtr;CallParseStructFuncPtr.RHS.Add(ObjectTerm);CallParseStructFuncPtr.RHS.Add(InputTermArray[i]);}FBlueprintCompiledStatement& CallAddSuffixFuncPtr = Context.AppendStatementForNode(MyNode);CallAddSuffixFuncPtr.Type = KCST_CallFunction;CallAddSuffixFuncPtr.FunctionToCall = AddSuffixFuncPtr;CallAddSuffixFuncPtr.RHS.Add(ObjectTerm);FBlueprintCompiledStatement& CallToByteArrayFuncPtr = Context.AppendStatementForNode(MyNode);CallToByteArrayFuncPtr.Type = KCST_CallFunction;CallToByteArrayFuncPtr.FunctionToCall = ToByteArrayFuncPtr;CallToByteArrayFuncPtr.LHS = ReturnValue;CallToByteArrayFuncPtr.RHS.Add(ObjectTerm);GenerateSimpleThenGoto(Context, *Node);}
};
Compiler其实就是在编译时就根据生成字节码,调用到这个Node时就会根据生成的字节码调用。
1.
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override{UK2Node_ConvertScriptCallValue* MyNode = CastChecked<UK2Node_ConvertScriptCallValue>(Node);{UEdGraphPin* ReturnPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);UEdGraphPin* ReturnPinToTry = FEdGraphUtilities::GetNetFromPin(ReturnPin);FBPTerminal* Term =Context.CreateLocalTerminalFromPinAutoChooseScope(ReturnPin, Context.NetNameMap->MakeValidName(ReturnPin));Term->bPassedByReference = false;Term->Source = Node;Context.NetMap.Add(ReturnPin, Term);}ObjectTerm = Context.CreateLocalTerminal();ObjectTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Object;ObjectTerm->Source = Node;ObjectTerm->Name = Context.NetNameMap->MakeValidName(Node) + TEXT("_TempObject");FNodeHandlingFunctor::RegisterNets(Context, Node);}
创建两个FBPTerminal,FBPTerminal在这一层是代表的其实就是变量。
创建一个返回值的FBPTerminal并添加进Context.NetMap。
创建一个Object类型临时FBPTerminal。
2.
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override{UK2Node_ConvertScriptCallValue* MyNode = CastChecked<UK2Node_ConvertScriptCallValue>(Node);UEdGraphPin* ReturnPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);FBPTerminal* ReturnValue = nullptr;FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin);if (ReturnTerm != nullptr && *ReturnTerm != nullptr)ReturnValue = *ReturnTerm;TArray<UEdGraphPin*> ValuesPins = MyNode->GetValuePin();TArray<FBPTerminal*> InputTermArray;for (int i = 0; i < ValuesPins.Num(); i++){UEdGraphPin* InputPin = ValuesPins[i];if(!InputPin)continue;UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(InputPin);FBPTerminal** pInputTerm = Context.NetMap.Find(PinToTry);if (pInputTerm != nullptr && *pInputTerm != nullptr)InputTermArray.Add(*pInputTerm);}UClass* ParseValueClass = UScriptCallParseValue::StaticClass();UFunction* CreateFuncPtr = FindUField<UFunction>(ParseValueClass, "CreatePraseValueObject");UFunction* AddPrefixFuncPtr = FindUField<UFunction>(ParseValueClass, "AddPrefixToArchive");UFunction* AddSuffixFuncPtr = FindUField<UFunction>(ParseValueClass, "AddSuffixToArchive");UFunction* ObjectParseValue2BufferFuncPtr = FindUField<UFunction>(ParseValueClass, "ObjectParseValue2Buffer");UFunction* ToByteArrayFuncPtr = FindUField<UFunction>(ParseValueClass, "ArchiveToBuffer");FBlueprintCompiledStatement& CallCreateFuncPtr = Context.AppendStatementForNode(MyNode);CallCreateFuncPtr.Type = KCST_CallFunction;CallCreateFuncPtr.FunctionToCall = CreateFuncPtr;CallCreateFuncPtr.LHS = ObjectTerm;FBlueprintCompiledStatement& CallAddPrefixFuncPtr = Context.AppendStatementForNode(MyNode);CallAddPrefixFuncPtr.Type = KCST_CallFunction;CallAddPrefixFuncPtr.FunctionToCall = AddPrefixFuncPtr;CallAddPrefixFuncPtr.RHS.Add(ObjectTerm);for (int i = 0; i < InputTermArray.Num(); i++){FBlueprintCompiledStatement& CallParseStructFuncPtr = Context.AppendStatementForNode(MyNode);CallParseStructFuncPtr.Type = KCST_CallFunction;CallParseStructFuncPtr.FunctionToCall = ObjectParseValue2BufferFuncPtr;CallParseStructFuncPtr.RHS.Add(ObjectTerm);CallParseStructFuncPtr.RHS.Add(InputTermArray[i]);}FBlueprintCompiledStatement& CallAddSuffixFuncPtr = Context.AppendStatementForNode(MyNode);CallAddSuffixFuncPtr.Type = KCST_CallFunction;CallAddSuffixFuncPtr.FunctionToCall = AddSuffixFuncPtr;CallAddSuffixFuncPtr.RHS.Add(ObjectTerm);FBlueprintCompiledStatement& CallToByteArrayFuncPtr = Context.AppendStatementForNode(MyNode);CallToByteArrayFuncPtr.Type = KCST_CallFunction;CallToByteArrayFuncPtr.FunctionToCall = ToByteArrayFuncPtr;CallToByteArrayFuncPtr.LHS = ReturnValue;CallToByteArrayFuncPtr.RHS.Add(ObjectTerm);GenerateSimpleThenGoto(Context, *Node);}
1)从
FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin);
找到返回值的FBPTerminal。
注意这里的区别,返回值是要我们自己创建FBPTerminal并使用。
但是输入的其实找的是跟Pin连线的那个Pin对应的PBPTerminal。
UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(InputPin);FBPTerminal** pInputTerm = Context.NetMap.Find(PinToTry);
2)
FBlueprintCompiledStatement& CallCreateFuncPtr = Context.AppendStatementForNode(MyNode);CallCreateFuncPtr.Type = KCST_CallFunction;CallCreateFuncPtr.FunctionToCall = CreateFuncPtr;CallCreateFuncPtr.LHS = ObjectTerm;
FBlueprintCompiledStatement& CallCreateFuncPtr = Context.AppendStatementForNode(MyNode);
创建FBlueprintCompiledStatement,并注册进Context。
FBlueprintCompiledStatement相当于操作,Type为KCST_CallFunction代表函数操作。FunctionToCall表示调用的Function。LHS是左值,即返回值,RHS这里是函数参数。
代码:
.h
?
#pragma once
#include "CoreMinimal.h"
#include "UObject/ObjectMacros.h"
#include "K2Node.h"
#include "EdGraph/EdGraphPin.h"
#include "K2Node_AddPinInterface.h"
#include "K2Node_ConvertScriptCallValue.generated.h"UCLASS()
class SEASHELLDEVELOPER_API UK2Node_ConvertScriptCallValue : public UK2Node, public IK2Node_AddPinInterface
{GENERATED_UCLASS_BODY()
public:// UEdGraphNode interfacevirtual FText GetTooltipText() const override { return FText::FromString(TEXT("a convert script call valut to buffer node")); }virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override { return FText::FromString(TEXT("Convert ScriptCall Value")); }virtual void AllocateDefaultPins() override;virtual void PostReconstructNode() override;virtual void GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const override;// End of UEdGraphNode interface// UK2Node interfacevirtual FText GetMenuCategory() const { return FText::FromString(TEXT("ScriptCall")); }virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;FNodeHandlingFunctor* CreateNodeHandler(FKismetCompilerContext& CompilerContext) const;virtual void NotifyPinConnectionListChanged(UEdGraphPin* Pin) override;void RemoveInputPin(UEdGraphPin* Pin);// IK2Node_AddPinInterface interfacevirtual void AddInputPin() override;virtual bool CanAddPin() const override{return true;}// End of IK2Node_AddPinInterface interfaceTArray<UEdGraphPin*> GetValuePin();UPROPERTY()int32 NumInputs;private:bool CanRemovePin(const UEdGraphPin* Pin) const;void SyncPinNames();virtual FName GetPinName(int32 PinIndex) const;
};?
.cpp
#include "K2Nodes/K2Node_ConvertScriptCallValue.h"//#include "K2Node_DoOnceMultiInput.h"
//#include "Textures/SlateIcon.h"
//#include "Framework/Commands/UIAction.h"
#include "ToolMenus.h"
#include "EdGraphSchema_K2.h"
//#include "K2Node_AssignmentStatement.h"
//#include "K2Node_TemporaryVariable.h"
#include "Kismet2/BlueprintEditorUtils.h"//#include "ScopedTransaction.h"
#include "KismetCompiler.h"
#include "BlueprintNodeSpawner.h"
#include "EditorCategoryUtils.h"
#include "BlueprintActionDatabaseRegistrar.h"#include "EdGraphUtilities.h"
#include "ScriptCall/ScriptCallParseValue.h"
#include "GraphEditorActions.h"#define LOCTEXT_NAMESPACE "K2Node"class FKCHandler_ConvertScriptCallValue : public FNodeHandlingFunctor
{
protected:FBPTerminal* ObjectTerm = nullptr;public:FKCHandler_ConvertScriptCallValue(FKismetCompilerContext& InCompilerContext) : FNodeHandlingFunctor(InCompilerContext){}virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override{UK2Node_ConvertScriptCallValue* MyNode = CastChecked<UK2Node_ConvertScriptCallValue>(Node);{UEdGraphPin* ReturnPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);UEdGraphPin* ReturnPinToTry = FEdGraphUtilities::GetNetFromPin(ReturnPin);FBPTerminal* Term =Context.CreateLocalTerminalFromPinAutoChooseScope(ReturnPin, Context.NetNameMap->MakeValidName(ReturnPin));Term->bPassedByReference = false;Term->Source = Node;Context.NetMap.Add(ReturnPin, Term);}ObjectTerm = Context.CreateLocalTerminal();ObjectTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Object;ObjectTerm->Source = Node;ObjectTerm->Name = Context.NetNameMap->MakeValidName(Node) + TEXT("_TempObject");FNodeHandlingFunctor::RegisterNets(Context, Node);}virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override{UK2Node_ConvertScriptCallValue* MyNode = CastChecked<UK2Node_ConvertScriptCallValue>(Node);UEdGraphPin* ReturnPin = Context.FindRequiredPinByName(Node, UEdGraphSchema_K2::PN_ReturnValue, EGPD_Output);FBPTerminal* ReturnValue = nullptr;FBPTerminal** ReturnTerm = Context.NetMap.Find(ReturnPin);if (ReturnTerm != nullptr && *ReturnTerm != nullptr)ReturnValue = *ReturnTerm;TArray<UEdGraphPin*> ValuesPins = MyNode->GetValuePin();TArray<FBPTerminal*> InputTermArray;for (int i = 0; i < ValuesPins.Num(); i++){UEdGraphPin* InputPin = ValuesPins[i];if(!InputPin)continue;UEdGraphPin* PinToTry = FEdGraphUtilities::GetNetFromPin(InputPin);FBPTerminal** pInputTerm = Context.NetMap.Find(PinToTry);if (pInputTerm != nullptr && *pInputTerm != nullptr)InputTermArray.Add(*pInputTerm);}UClass* ParseValueClass = UScriptCallParseValue::StaticClass();UFunction* CreateFuncPtr = FindUField<UFunction>(ParseValueClass, "CreatePraseValueObject");UFunction* AddPrefixFuncPtr = FindUField<UFunction>(ParseValueClass, "AddPrefixToArchive");UFunction* AddSuffixFuncPtr = FindUField<UFunction>(ParseValueClass, "AddSuffixToArchive");UFunction* ObjectParseValue2BufferFuncPtr = FindUField<UFunction>(ParseValueClass, "ObjectParseValue2Buffer");UFunction* ToByteArrayFuncPtr = FindUField<UFunction>(ParseValueClass, "ArchiveToBuffer");FBlueprintCompiledStatement& CallCreateFuncPtr = Context.AppendStatementForNode(MyNode);CallCreateFuncPtr.Type = KCST_CallFunction;CallCreateFuncPtr.FunctionToCall = CreateFuncPtr;CallCreateFuncPtr.LHS = ObjectTerm;FBlueprintCompiledStatement& CallAddPrefixFuncPtr = Context.AppendStatementForNode(MyNode);CallAddPrefixFuncPtr.Type = KCST_CallFunction;CallAddPrefixFuncPtr.FunctionToCall = AddPrefixFuncPtr;CallAddPrefixFuncPtr.RHS.Add(ObjectTerm);for (int i = 0; i < InputTermArray.Num(); i++){FBlueprintCompiledStatement& CallParseStructFuncPtr = Context.AppendStatementForNode(MyNode);CallParseStructFuncPtr.Type = KCST_CallFunction;CallParseStructFuncPtr.FunctionToCall = ObjectParseValue2BufferFuncPtr;CallParseStructFuncPtr.RHS.Add(ObjectTerm);CallParseStructFuncPtr.RHS.Add(InputTermArray[i]);}FBlueprintCompiledStatement& CallAddSuffixFuncPtr = Context.AppendStatementForNode(MyNode);CallAddSuffixFuncPtr.Type = KCST_CallFunction;CallAddSuffixFuncPtr.FunctionToCall = AddSuffixFuncPtr;CallAddSuffixFuncPtr.RHS.Add(ObjectTerm);FBlueprintCompiledStatement& CallToByteArrayFuncPtr = Context.AppendStatementForNode(MyNode);CallToByteArrayFuncPtr.Type = KCST_CallFunction;CallToByteArrayFuncPtr.FunctionToCall = ToByteArrayFuncPtr;CallToByteArrayFuncPtr.LHS = ReturnValue;CallToByteArrayFuncPtr.RHS.Add(ObjectTerm);GenerateSimpleThenGoto(Context, *Node);}
};UK2Node_ConvertScriptCallValue::UK2Node_ConvertScriptCallValue(const FObjectInitializer& ObjectInitializer) :Super(ObjectInitializer)
{NumInputs = 0;
}void UK2Node_ConvertScriptCallValue::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{UClass* ActionKey = GetClass();if (ActionRegistrar.IsOpenForRegistration(ActionKey)){UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());check(NodeSpawner != nullptr);ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);}
}void UK2Node_ConvertScriptCallValue::AllocateDefaultPins()
{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 pinUEdGraphNode::FCreatePinParams PinParams;PinParams.ContainerType = EPinContainerType::Array;PinParams.bIsReference = false;CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Byte, UEdGraphSchema_K2::PN_ReturnValue, PinParams);for (int32 i = 0; i < NumInputs; ++i){CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, GetPinName(i));}
}void UK2Node_ConvertScriptCallValue::PostReconstructNode()
{TArray<UEdGraphPin*> ValuesPins = GetValuePin();for (int i = 0; i < ValuesPins.Num(); i++){UEdGraphPin* Pin = ValuesPins[i];NotifyPinConnectionListChanged(Pin);}
}FNodeHandlingFunctor* UK2Node_ConvertScriptCallValue::CreateNodeHandler(FKismetCompilerContext& CompilerContext) const
{return new FKCHandler_ConvertScriptCallValue(CompilerContext);
}void UK2Node_ConvertScriptCallValue::AddInputPin()
{CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, GetPinName(NumInputs));NumInputs++;
}TArray<UEdGraphPin*> UK2Node_ConvertScriptCallValue::GetValuePin()
{TArray<UEdGraphPin*> RetutrnValues; for (UEdGraphPin* Pin : Pins){if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec && Pin->Direction != EGPD_Output)RetutrnValues.Add(Pin);}return RetutrnValues;
}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))));}}}
}void UK2Node_ConvertScriptCallValue::RemoveInputPin(UEdGraphPin * Pin)
{FScopedTransaction Transaction(FText::FromString("ConvertScriptCallValue_RemoveInputPin"));Modify();--NumInputs;RemovePin(Pin);SyncPinNames();FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}void UK2Node_ConvertScriptCallValue::SyncPinNames()
{int32 CurrentNumParentPins = 0;for (int32 PinIndex = 0; PinIndex < Pins.Num(); ++PinIndex){UEdGraphPin*& CurrentPin = Pins[PinIndex];if (CurrentPin->Direction == EGPD_Input &&CurrentPin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec){const FName OldName = CurrentPin->PinName;const FName ElementName = GetPinName(CurrentNumParentPins++);CurrentPin->Modify();CurrentPin->PinName = ElementName;}}
}FName UK2Node_ConvertScriptCallValue::GetPinName(const int32 PinIndex) const
{return *FString::Printf(TEXT("[%d]"), PinIndex);
}void UK2Node_ConvertScriptCallValue::NotifyPinConnectionListChanged(UEdGraphPin* Pin)
{Super::NotifyPinConnectionListChanged(Pin);if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec || Pin->Direction == EGPD_Output)return;// If the pin is linked, make sure the other wildcard pins matchif (Pin->LinkedTo.Num() > 0){UEdGraphPin* LinkPin = Pin->LinkedTo[0];if (Pin->PinType != LinkPin->PinType){Pin->PinType = MoveTemp(LinkPin->PinType);GetGraph()->NotifyGraphChanged();}}else if (Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Wildcard){Pin->PinType.ResetToDefaults();Pin->PinType.PinCategory = UEdGraphSchema_K2::PC_Wildcard;GetGraph()->NotifyGraphChanged();}
}bool UK2Node_ConvertScriptCallValue::CanRemovePin(const UEdGraphPin* Pin) const
{return (Pin && (INDEX_NONE != Pins.IndexOfByKey(Pin)) && (EEdGraphPinDirection::EGPD_Input == Pin->Direction));
}#undef LOCTEXT_NAMESPACE