アンリアる! C++入門編 ~対話形式で学ぶUnreal Engine~
BOOTHでUnreal Engine C++入門書を販売していますUnreal Engine上でC++を使って開発するために必要となる基礎知識を一冊の本にまとめた本です。 対話形式によるわかりやすい説明を目指しました。無料の試し読みも可能ですので、ぜひ読んでみてください!
[UE5] Create Your Own Blueprint Node by Inheriting K2Node (1) Basic

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.

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.

  1. Place the directory MyDivide into Plugins in your Unreal Engine project directory.
  2. Right click on the .uproject file and create a project file for Visual Studio.
  3. Open the project file and build the project.
  4. 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.

  1. Inherit class UK2Node.
  2. Setup basic information.
  3. Add pins.
  4. Make Pure function.
  5. Define the process of data pin.

Inherit class UK2Node

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.

Setup Basic Information

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.

Basic Information Settings

Add Pins

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.

Make Pure function

We can make the node Pure function by returning True from the member function IsNodePure.

bool UK2Node_MyDivide::IsNodePure() const
{
    return true;
}

Define the Process of Data Pin

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.

Implement in 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.

Search Node "MyDivide"

Select and place the node "MyDivide" in the category "Math".

Create an Actor with appropriate input values.

Create Actor

When the created Actor is placed in the level, we can see that the quotient and remainder by division.

Execute Node "MyDivide"