When you create your own Blueprint node in the Unreal Engine, creating a library function is a one of the way.
However, there is a limitation to create a Blueprint node with library functions.
For example, it is not possible to create complex nodes with execution control, such as "Switch on Int".
This article explains how to create a Blueprint node by inheriting from the class UK2Node.
Creating a Blueprint node is an in-depth content, so we will divide the topics.
- Create Your Own Blueprint Node by Inheriting K2Node (1) Basic <— This article
- Create Your Own Blueprint Node by Inheriting K2Node (2) Execution Pin
- Create Your Own Blueprint Node by Inheriting K2Node (3) Menu
- Create Your Own Blueprint Node by Inheriting K2Node (4) UI
目次
Source Code
The complete source code is available on GitHub.
[https://github.com/colory-games/techblog-sample-projects/tree/main/nutti/20220622](https://github.com/colory-games/techblog- sample-projects/tree/main/nutti/20220622)
To run the source code, follow these steps.
- Place the directory
MyDivide
intoPlugins
in your Unreal Engine project directory. - Right click on the
.uproject
file and create a project file for Visual Studio. - Open the project file and build the project.
- Run Unreal Engine from the
.uproject
file and activate the "MyDivide" plugin.
Specification of the Blueprint Node
The specification of the Blueprint node "MyDivide" is as follows.
- Takes two integers "A" and "B" as input and outputs the quotient "Quotient" of A divided by B and the remainder "Remainder".
- No execution pin. (Pure function)
The node we are going to create is a node that can be created using the Blueprint Editor.
However, we will create it using the class UK2Node.
Create a Module
In this section, the source code structure is designed to provide the Blueprint node "MyDivide" in plugin format.
First, we will show the contents of the Build.cs
file.
The necessary modules to create a Blueprint node are added.
using UnrealBuildTool;
public class MyDivide : ModuleRules
{
public MyDivide(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(new string[]{
"Core",
"CoreUObject",
"Engine"
});
PrivateDependencyModuleNames.AddRange(new string[]{
"BlueprintGraph",
"GraphEditor",
"KismetCompiler",
"SlateCore",
"UnrealEd"
});
}
}
Also, add the following entry to Modules
in the .uplugin
file.
{
"Name": "MyDivide",
"Type": "UncookedOnly",
"LoadingPhase": "PreLoadingScreen"
}
Create a Blueprint Node
We can create a Blueprint node by the following steps.
- Inherit class
UK2Node
. - Setup basic information.
- Add pins.
- Make Pure function.
- Define the process of data pin.
Blueprint nodes are defined by inheriting from class UK2Node
.
Create a header file K2Node_MyDivide.h
and define the class UK2Node_MyDivide
.
UCLASS(meta = (Keywords = "MyDivide Divide"))
class UK2Node_MyDivide : public UK2Node
{
GENERATED_BODY()
// Overrided from UEdGraphNode.
virtual void AllocateDefaultPins() override;
virtual FText GetTooltipText() const override;
virtual FLinearColor GetNodeTitleColor() const override;
virtual FText GetNodeTitle(ENodeTitleType::Type TitleType) const override;
virtual FSlateIcon GetIconAndTint(FLinearColor& OutColor) const override;
// Overrided from UK2Node.
virtual bool IsNodePure() const override;
virtual void GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const override;
virtual FText GetMenuCategory() const override;
void ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) override;
};
The following member functions are declared in the class UK2Node_MyDivide
.
Member Function | Role |
---|---|
AllocateDefaultPins |
Allocate default pins. |
GetTooltipText |
Set the text of the Tooltip. |
GetNodeTitleColor |
Set the color of the title section. |
GetNodeTitle |
Set the text to display in the title section. |
GetIconAndTint |
Set the icon to display in the title section. |
IsNodePure |
If true is returned, the node will be a Pure function. |
GetMenuActions |
Make nodes available from the Blueprint editor. |
GetMenuCategory |
Set the category for the node search menu. |
ExpandNode |
Add/Remove node at compilation time. |
Create the definitions of the member functions you declared.
Create a source file K2Node_MyDivide.cpp
and define each member function.
For details, refer to the comments in each function.
FText UK2Node_MyDivide::GetTooltipText() const
{
// Returns text to be displayed in Tooltip.
return LOCTEXT("MyDivide_Tooltip", "MyDivide\nGet quotient and remainder from dividing two integer");
}
FLinearColor UK2Node_MyDivide::GetNodeTitleColor() const
{
// The color of the title section is the same color as the integer type (Integer) pin.
return GetDefault()->IntPinTypeColor;
}
FText UK2Node_MyDivide::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
// Returns text to be displayed in the title section.
return LOCTEXT("MyDivide", "MyDivide");
}
FSlateIcon UK2Node_MyDivide::GetIconAndTint(FLinearColor& OutColor) const
{
// Display the function icon in the title section.
static FSlateIcon Icon("EditorStyle", "Kismet.AllClasses.FunctionIcon");
return Icon;
}
void UK2Node_MyDivide::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const
{
UClass* ActionKey = GetClass();
if (ActionRegistrar.IsOpenForRegistration(ActionKey))
{
UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass());
check(NodeSpawner != nullptr);
ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner);
}
}
FText UK2Node_MyDivide::GetMenuCategory() const
{
// Register the category "Math".
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Math);
}
The member function GetMenuActions
adds the node "MyDivide" to the right-clicked menu in the Blueprint Editor.
Note that the node "MyDivide" will not appear in the menu if you forget to implement the member function GetMenuActions
.
This process is same when you create a Blueprint node, so you can reuse this implementation.
Also, by implementing the member function GetMenuCategory
, the node MyDivide
is registered in the category Math
.
Add the following pins according to the specification.
Pin Name | Input/Output | Role |
---|---|---|
A | Input | Input of integer. |
B | Input | Input of integer. |
Quotient | Output | Quotient of A divided by B. |
Remainder | Output | Remainder of A divided by B. |
We need to add those pins.
AllocateDefaultPins
is the member funciton which defines the process of adding pins to the node.
namespace
{
static const FName APinName(TEXT("A"));
static const FName BPinName(TEXT("B"));
static const FName QuotientPinName(TEXT("Quotient"));
static const FName RemainderPinName(TEXT("Remainder"));
}
void UK2Node_MyDivide::AllocateDefaultPins()
{
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, APinName);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, BPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Int, QuotientPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Int, RemainderPinName);
}
We can add pins by calling the member function CreatePin
.
The first argument specifies the input/output direction of the pin.
Specify EGPD_Input
for input or EGPD_Output
for output.
The second argument specifies the pin type.
Since all pins are integer type (Integer), specify UEdGraphSchema_K2::PC_Int
.
The third argument specifies the pin name.
We can make the node Pure function by returning True
from the member function IsNodePure
.
bool UK2Node_MyDivide::IsNodePure() const
{
return true;
}
Finally, define the process of the data pins.
The process of data pin must be defined as a computational graph.
The node receives two integers "A" and "B" as input and output the quotient "Quotient" of A divided by B and the remainder "Remainder".
We will show the code to realize this in a Blueprint.
- Connect input pins "A" and "B" to the input of the "/" node and its output to the output pin "Quotient".
- Connect input pins "A" and "B" to the input of the "%" node and its output to the output pin "Remainder".
The "/" node corresponds to a call the library function "Divide_IntInt" of "UKismetMathLibrary".
The "%" node corresponds to a call the library function "Percent_IntInt" of "UKismetMathLibrary".
Therefore, it is sufficient to define a process in the member function ExpandNode
to realize the following operations.
- Call the library function "Divide_IntInt" with the input pins "A" and "B" as inputs, and connect the result to the output pin "Quotient".
- Call the library function `Percent_IntInt’ with the input pins "A" and "B" as inputs, and connect the result to the output pin "Remainder
We will show the source code of the member function ExpandNode
.
void UK2Node_MyDivide::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
// This equals to the placement of "/" node.
UK2Node_CallFunction* CallDivideFunction = CompilerContext.SpawnIntermediateNode(this, SourceGraph);
CallDivideFunction->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetMathLibrary, Divide_IntInt)));
CallDivideFunction->AllocateDefaultPins();
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(QuotientPinName), *CallDivideFunction->GetReturnValuePin());
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(APinName), *CallDivideFunction->FindPinChecked(TEXT("A")));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(BPinName), *CallDivideFunction->FindPinChecked(TEXT("B")));
// This equals to the placement of "%" node.
UK2Node_CallFunction* CallPercentFunction = CompilerContext.SpawnIntermediateNode(this, SourceGraph);
CallPercentFunction->SetFromFunction(UKismetMathLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UKismetMathLibrary, Percent_IntInt)));
CallPercentFunction->AllocateDefaultPins();
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(RemainderPinName), *CallPercentFunction->GetReturnValuePin());
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(APinName), *CallPercentFunction->FindPinChecked(TEXT("A")));
CompilerContext.MovePinLinksToIntermediate(*FindPinChecked(BPinName), *CallPercentFunction->FindPinChecked(TEXT("B")));
}
You can add a node to call a library function by calling the member function SpawnIntermediateNode
of the CompilerContext
with template parameter UK2Node_CallFunction
.
The member function SetFromFunction
of UK2Node_CallFunction
is used to specify the library function to be called.
We can see that the first added node (CallDivideFunction
) is same as placing the "/" node.
Since the pins for the "/" node have not been added, we need to call the member function AllocateDefaultPins
to add them.
The member function MovePinLinksToIntermediate
of CompilerContext
connects the two specified pins.
We will connect the pins as follows.
- The output pin "Quotient" and the output pin of
CallDivideFunction
. - Input pin "A" and the input pin "A" of
CallDivideFunction
. - Input pin "B" and the input pin "B" of
CallDivideFunction
.
Add the "%" node in the same way.
The implementation is finished now.
Verify that the source code can be built correctly.
Use the Created Node
Let’s use the node you created. Open the Blueprint Editor, right-click to display the menu, and enter "mydivide" in the search box.
Select and place the node "MyDivide" in the category "Math".
Create an Actor with appropriate input values.
When the created Actor is placed in the level, we can see that the quotient and remainder by division.