i! I am working on some custom nodes or at least trying to. I am a fairly competent coder that is more used C# and Java making the transition into C++.
I was following a Tutorial by Nicholas Ferrar, as he is seemingly the only person on the internet that’s ever posted a fully functioning k2Node script, Cpp and .h and supporting classes. I am doing this because I need some nodes to have dynamic numbered/functioning output and exec pins for some nodes in my system, and simply exposing functions in blueprints doesn’t give me these dynamic functionalities.
I am sure the problem stems from him having made this in UE4 and the project I am working for right now is using UE5.
[text](https://github.com/nFerrar/K2Node_Intro)
Anyway, at some point I tried using a function he wrote called FindSetterFunction, which looksl ike this:
UFunction * UMyK2Node::FindSetterFunctionByType(FEdGraphPinType& PinType)
{
UClass* LibraryClass = UMyK2Node::StaticClass();
FName FunctionName = NAME_None;
UFunction* Function = nullptr;
//this should really be in a const FNAME
//here goes a bunch of IFs to check the pin type and set FUNCTION NAME to its NAME
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
{
FunctionName = FSetterFunctionNames::FloatSetterName;
}
return Function;
}
This is my console on after compilation:
> Build started...
1>------ Build started: Project: MyProject, Configuration: Development_Editor x64 ------
1>Using bundled DotNet SDK
1>Log file: C:\Users\jarri\AppData\Local\UnrealBuildTool\Log.txt
1>Building MyProjectEditor...
1>Using Visual Studio 2019 14.29.30146 toolchain (C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.29.30133) and Windows 10.0.20348.0 SDK (C:\Program Files (x86)\Windows Kits\10).
1>[Adaptive Build] Excluded from MyProject unity file: MyK2Node.cpp, MyK2NodeData.cpp, MyProject.cpp, MyProjectGameModeBase.cpp
1>Determining max actions to execute in parallel (8 physical cores, 16 logical cores)
1> Executing up to 8 processes, one per physical core
1> Requested 1.5 GB free memory per action, 4.91 GB available: limiting max parallel actions to 3
1>Building 4 actions with 3 processes...
1>[1/4] Compile MyK2Node.cpp
1>[2/4] Link UnrealEditor-MyProject-0012.lib
1> Creating library C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.lib and object C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.exp
1>[3/4] Link UnrealEditor-MyProject-0012.dll
1> Creating library C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.suppressed.lib and object C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Intermediate\Build\Win64\UnrealEditor\Development\MyProject\UnrealEditor-MyProject-0012.suppressed.exp
1>MyK2Node.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: static void __cdecl FEdGraphToken::Create(class UObject const *,class FCompilerResultsLog *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &)" (__imp_?Create@FEdGraphToken@@SAXPEBVUObject@@PEAVFCompilerResultsLog@@AEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z) referenced in function "protected: void __cdecl FCompilerResultsLog::Tokenize<class UMyK2Node *>(wchar_t const *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &,class UMyK2Node *)" (??$Tokenize@PEAVUMyK2Node@@$V@FCompilerResultsLog@@IEAAXPEB_WAEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@PEAVUMyK2Node@@@Z)
1> Hint on symbols that are defined and could potentially match:
1> "__declspec(dllimport) public: static class TSharedRef<class FTextToken,1> __cdecl FTextToken::Create(class FText const &)" (__imp_?Create@FTextToken@@SA?AV?$TSharedRef@VFTextToken@@$00@@AEBVFText@@@Z)
1> "__declspec(dllimport) public: static class TSharedRef<class FTokenizedMessage,1> __cdecl FTokenizedMessage::Create(enum EMessageSeverity::Type,class FText const &)" (__imp_?Create@FTokenizedMessage@@SA?AV?$TSharedRef@VFTokenizedMessage@@$00@@W4Type@EMessageSeverity@@AEBVFText@@@Z)
1> "__declspec(dllimport) public: static class UBlueprintNodeSpawner * __cdecl UBlueprintNodeSpawner::Create(class TSubclassOf<class UEdGraphNode>,class UObject *,class TDelegate<void __cdecl(class UEdGraphNode *,bool),struct FDefaultDelegateUserPolicy>)" (__imp_?Create@UBlueprintNodeSpawner@@SAPEAV1@V?$TSubclassOf@VUEdGraphNode@@@@PEAVUObject@@V?$TDelegate@$A6AXPEAVUEdGraphNode@@_N@ZUFDefaultDelegateUserPolicy@@@@@Z)
1>MyK2Node.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) public: void __cdecl FCompilerResultsLog::NotifyIntermediateObjectCreation(class UObject *,class UObject *)" (__imp_?NotifyIntermediateObjectCreation@FCompilerResultsLog@@QEAAXPEAVUObject@@0@Z)
1>MyK2Node.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) protected: void __cdecl FCompilerResultsLog::InternalLogMessage(class FName,class TSharedRef<class FTokenizedMessage,1> const &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > const &)" (__imp_?InternalLogMessage@FCompilerResultsLog@@IEAAXVFName@@AEBV?$TSharedRef@VFTokenizedMessage@@$00@@AEBV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z) referenced in function "public: virtual void __cdecl UMyK2Node::ExpandNode(class FKismetCompilerContext &,class UEdGraph *)" (?ExpandNode@UMyK2Node@@UEAAXAEAVFKismetCompilerContext@@PEAVUEdGraph@@@Z)
1>MyK2Node.cpp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) protected: void __cdecl FCompilerResultsLog::Tokenize(wchar_t const *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &)" (__imp_?Tokenize@FCompilerResultsLog@@IEAAXPEB_WAEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@@Z) referenced in function "protected: void __cdecl FCompilerResultsLog::Tokenize<class UMyK2Node *>(wchar_t const *,class FTokenizedMessage &,class TArray<class UEdGraphNode *,class TSizedDefaultAllocator<32> > &,class UMyK2Node *)" (??$Tokenize@PEAVUMyK2Node@@$V@FCompilerResultsLog@@IEAAXPEB_WAEAVFTokenizedMessage@@AEAV?$TArray@PEAVUEdGraphNode@@V?$TSizedDefaultAllocator@$0CA@@@@@PEAVUMyK2Node@@@Z)
1>MyK2Node.cpp.obj : error LNK2001: unresolved external symbol "__declspec(dllimport) protected: void __cdecl FKismetCompilerContext::AutoAssignNodePosition(class UEdGraphNode *)" (__imp_?AutoAssignNodePosition@FKismetCompilerContext@@IEAAXPEAVUEdGraphNode@@@Z)
1>C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\Binaries\Win64\UnrealEditor-MyProject-0012.dll : fatal error LNK1120: 5 unresolved externals
1>C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Microsoft\VC\v160\Microsoft.MakeFile.Targets(45,5): error MSB3073: The command ""D:\Program Files\Epic Games\UE_5.0\Engine\Build\BatchFiles\Build.bat" MyProjectEditor Win64 Development -Project="C:\Users\jarri\OneDrive\Documents\Unreal Projects\MyProject\MyProject.uproject" -WaitMutex -FromMsBuild" exited with code 6.
1>Done building project "MyProject.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
PS: If anyone doesn’t know how to solve this specific problem, and has a better resource for learning how UE handles making nodes (Custom dialog system, and no I do not want to use a behaviour tree) please let me know!
I am making this node strictly for practice, and this is as car as I have gotten. As of right now this code draws a small featureless node with the nodes name on it.
Thanks!
I was hoping this would finally draw a full node to the graph and draw the pins onto the graph. `
Full code so far:
.h
#pragma once
#include "CoreMinimal.h"
#include "K2Node.h"
#include "MyK2Node.generated.h"
/**
*
*/
UCLASS()
class MYPROJECT_API UMyK2Node : public UK2Node
{
GENERATED_BODY()
public:
virtual void AllocateDefaultPins() override;
virtual void ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType)const override;
virtual FText GetTooltipText() const override;
virtual FText GetMenuCategory() const override;
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
UEdGraphPin* GetThenPin() const;
UEdGraphPin* GetNewValuePin() const;
UEdGraphPin* GetReturnResultPin() const;
UEdGraphPin* GetReturnValuePin() const;
static UFunction* FindSetterFunctionByType(FEdGraphPinType& PinType);
};
.cpp
#include "MyK2Node.h"
#include "EdGraphSchema_K2.h"
#include "MyK2NodeData.h"
//probably boiler plate
#include "KismetCompiler.h"
#include "BlueprintActionDatabaseRegistrar.h"
#include "BlueprintNodeSpawner.h"
#include "K2Node_CallFunction.h"
#define LOCTEXT_NAMESPACE "MYK2NODE"
struct FGetPinName
{
static const FName& GetTargetPinName()
{
static const FName TargetPinName(TEXT("Target"));
return TargetPinName;
}
static const FName& GetVarNamePinName()
{
static const FName VarNamePinName(TEXT("VarName"));
return VarNamePinName;
}
static const FName& GetNewValuePinName()
{
static const FName NewValuePinName(TEXT("NewValue"));
return NewValuePinName;
}
static const FName& GetOutputResultPinName()
{
static const FName OutputResultPinName(TEXT("bSuccess"));
return OutputResultPinName;
}
static const FName& GetOutputValuePinName()
{
static const FName OutputValuePinName(TEXT("NewValueResult"));
return OutputValuePinName;
}
};
//FText FName Handling
namespace FSetterFunctionNames
{
static const FName FloatSetterName(GET_FUNCTION_NAME_CHECKED(UMyK2NodeData, SetFloatByName));
};
// // // // // // // // // //
void UMyK2Node::AllocateDefaultPins()
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
/*Create our pins*/
// Execution pins
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Then);
//Input
UEdGraphNode::FCreatePinParams PinParams;
PinParams.bIsReference = true;
UEdGraphPin* InTargetPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UObject::StaticClass(), FGetPinName::GetTargetPinName(), PinParams);
UEdGraphPin* InVarNamePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Name, FGetPinName::GetVarNamePinName());
K2Schema->SetPinAutogeneratedDefaultValue(InVarNamePin, FName("My Var Name").ToString());
UEdGraphPin* InNewValuePin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Wildcard, FGetPinName::GetNewValuePinName(), PinParams);
//Output
UEdGraphPin* OutValidPin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Boolean, FGetPinName::GetOutputResultPinName());
K2Schema->SetPinAutogeneratedDefaultValueBasedOnType(OutValidPin);
UEdGraphPin* OutNewValuePin = CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Wildcard, FGetPinName::GetOutputValuePinName());
Super::AllocateDefaultPins();
}
// // // // // // // // //
FText UMyK2Node::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
return LOCTEXT("TestNode_Title", "Test Node");
}
FText UMyK2Node::GetTooltipText() const
{
return LOCTEXT("TestNode_Tooltip", "This is a test.");
}
FText UMyK2Node::GetMenuCategory() const
{
return LOCTEXT("TestNode_MenuCategory", "Dialog Node");
}
// // // // // // // // // // // // // //
void UMyK2Node::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
Super::ExpandNode(CompilerContext, SourceGraph);
UFunction* BlueprintFunction = FindSetterFunctionByType(GetNewValuePin()->PinType);
if (!BlueprintFunction)
{
CompilerContext.MessageLog.Error(*LOCTEXT("InvalidFunctionName", "The function has not been found.").ToString(), this);
return;
}
UK2Node_CallFunction* CallFunction = CompilerContext.SpawnIntermediateNode<UK2Node_CallFunction>(this, SourceGraph);
CallFunction->SetFromFunction(BlueprintFunction);
CallFunction->AllocateDefaultPins();
CompilerContext.MessageLog.NotifyIntermediateObjectCreation(CallFunction, this);
//Input
CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetTargetPinName()), *CallFunction->FindPinChecked(TEXT("Target")));
CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetVarNamePinName()), *CallFunction->FindPinChecked(TEXT("VarName")));
CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetNewValuePinName()), *CallFunction->FindPinChecked(TEXT("NewValue")));
//Output
CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetOutputValuePinName()), *CallFunction->FindPinChecked(TEXT("OutValue")));
CompilerContext.MovePinLinksToIntermediate(*FindPin(FGetPinName::GetOutputResultPinName()), *CallFunction->GetReturnValuePin());
//Exec pins
UEdGraphPin* NodeExec = GetExecPin();
UEdGraphPin* NodeThen = FindPin(UEdGraphSchema_K2::PN_Then);
UEdGraphPin* InternalExec = CallFunction->GetExecPin();
CompilerContext.MovePinLinksToIntermediate(*NodeExec, *InternalExec);
UEdGraphPin* InternalThen = CallFunction->GetThenPin();
CompilerContext.MovePinLinksToIntermediate(*NodeThen, *InternalThen);
//After we are done we break all links to this node (not the internally created one)
BreakAllNodeLinks();
}
void UMyK2Node::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
Super::GetMenuActions(ActionRegistrar);
UClass* Action = GetClass();
if (ActionRegistrar.IsOpenForRegistration(Action))
{
UBlueprintNodeSpawner* Spawner = UBlueprintNodeSpawner::Create(GetClass());
check(Spawner != nullptr);
ActionRegistrar.AddBlueprintAction(Action, Spawner);
}
}
UEdGraphPin* UMyK2Node::GetThenPin() const
{
const UEdGraphSchema_K2* K2Schema = GetDefault<UEdGraphSchema_K2>();
UEdGraphPin* Pin = FindPinChecked(UEdGraphSchema_K2::PN_Then);
check(Pin->Direction == EGPD_Output);
return Pin;
}
UEdGraphPin* UMyK2Node::GetNewValuePin() const
{
UEdGraphPin* Pin = FindPin(FGetPinName::GetNewValuePinName());
check(Pin == NULL || Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UMyK2Node::GetReturnResultPin() const
{
UEdGraphPin* Pin = FindPin(FGetPinName::GetNewValuePinName());
check(Pin == NULL || Pin->Direction == EGPD_Input);
return Pin;
}
UEdGraphPin* UMyK2Node::GetReturnValuePin() const
{
UEdGraphPin* Pin = FindPin(FGetPinName::GetOutputResultPinName());
check(Pin == NULL || Pin->Direction == EGPD_Output);
return Pin;;
}
UFunction * UMyK2Node::FindSetterFunctionByType(FEdGraphPinType& PinType)
{
UClass* LibraryClass = UMyK2Node::StaticClass();
FName FunctionName = NAME_None;
UFunction* Function = nullptr;
//this should really be in a const FNAME
//here goes a bunch of IFs to check the pin type and set FUNCTION NAME to its NAME
if (PinType.PinCategory == UEdGraphSchema_K2::PC_Float)
{
FunctionName = FSetterFunctionNames::FloatSetterName;
}
return Function;
}
#undef LOCTEXT_NAMESPACE