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

Blueprint node has a clean UI by default.
However, some Blueprint nodes provide own UI that users can use easier than default.
For example, the Blueprint node "Switch on Int" has a horizontal bar above the default execution pin.
This makes it easier to distinguish from conditional execution pin.

Blueprint node "Switch on Int" UI

In this article, we will modify the UI of the Blueprint node created in the Menu article.

Source Code

The complete source code is available on GitHub.

https://github.com/colory-games/techblog-sample-projects/tree/main/nutti/20220815

To run the source code, follow these steps.

  1. Place the directory MyManualSwitch 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 "MyManualSwitch" plugin.

UI Changes

Here is the modified UI for the Blueprint node "MyManualSwitch".

Blueprint node "MyManualSwitch" UI

You can see that a new horizontal bar and text that displays an execution destination, have been added below the execution pin on the input side.
Notice also that the text changes when the execution destination changes.

Create a Module

Create a module by following to the description in the Basic section.
For the concreate instructions to create a module, please refer to the article for the basic.

Change the UI of Blueprint Node

To change the UI of a Blueprint node, follow the below steps.

  1. Inherit from class SGraphNodeK2Base
  2. Configure basic information
  3. Define the UI construction procedure
  4. Associate the UI with the Blueprint node

1. Inherit from Class SGraphNodeK2Base

First define the class SGraphNodeMyManualSwitch, which extends the class SGraphNodeK2Base.

class SGraphNodeMyManualSwitch : public SGraphNodeK2Base
{
    SLATE_BEGIN_ARGS(SGraphNodeMyManualSwitch)
    {
    }
    SLATE_END_ARGS()
    void Construct(const FArguments& InArgs, UK2Node_MyManualSwitch* InNode);
    virtual void CreatePinWidgets() override;
};

The macro SLATE_BEGIN_ARGS and SLATE_END_ARGS define the specification of parameters that can be passed at the instatiation time.
But, we do not use them in this case.

The class SGraphNodeMyManualSwitch has two member functions.

Member Function Name Role
Construct Called at instantiation time.
Configure basic information here.
CreatePinWidgets Called when the Engine draw input/output pins.
The construction process of UI need to be defined here.

2. Configure Basic Information

Basic information can be configured by defining a configuration process in the member function Construct of class SGraphNodeMyManualSwitch.

void SGraphNodeMyManualSwitch::Construct(const FArguments& InArgs, UK2Node_MyManualSwitch* InNode)
{
    GraphNode = InNode;
    SetCursor(EMouseCursor::GrabHand);
    UpdateGraphNode();
}

GraphNode is a member variable defined in the parent class and will be used later to define the construction process of the UI.
It is common to assign the argument InNode.

Next, call the member function SetCursor to change the cursor to a hand icon when the mouse over a Blueprint node.
Note that the cursor icon can be changed depending on the value of the enumerated type EMouseCursor passed to the member function SetCursor.

Finally, call the member function UpdateGraphNode to notify that the UI of Blueprint node has been changed.

3. Define the UI Construction Procedure

Define the construction process of the UI by overriding the member function CreatePinWidgets of class SGraphNode.
The UI is constructed according to the Unreal Engine’s Slate UI Framework.

void SGraphNodeMyManualSwitch::CreatePinWidgets()
{
    UK2Node_MyManualSwitch* MyManualSwitch = CastChecked(GraphNode);
    // Add pin UI.
    for (auto It = GraphNode->Pins.CreateConstIterator(); It; ++It)
    {
        UEdGraphPin* Pin = *It;
        if (!Pin->bHidden)
        {
            TSharedPtr NewPin = FNodeFactory::CreatePinWidget(Pin);
            check(NewPin.IsValid());
            AddPin(NewPin.ToSharedRef());
        }
    }
    bool bExecuteA = MyManualSwitch->GetSwitchValue();
    // Add horizontal bar.
    LeftNodeBox->AddSlot()
        .AutoHeight()
        .HAlign(HAlign_Left)
        .VAlign(VAlign_Center)
        [
            SNew(SImage)
            .Image(FEditorStyle::GetBrush("Graph.Pin.DefaultPinSeparator"))
        ];
    // Add text.
    LeftNodeBox->AddSlot()
        .AutoHeight()
        .HAlign(HAlign_Left)
        .VAlign(VAlign_Center)
        .Padding(10.0f, 5.0f)
        [
            SNew(STextBlock)
            .Text(FText::FromString(bExecuteA ? "Execute A" : "Execute B"))
        ];
}

We will not discuss the Slate UI framework in detail here, but explain two important things.

Add Pins

New pin can be created with the member function CreatePinWidget of class FNodeFactory.
Then, it can be added to the UI of a Blueprint node by calling the member function AddPin.
These member functions are the easiest way to implement if you use the default UI.
The member function AddPin automatically determines the location of the UI to be added based on the input/output of the pin.

LeftNodeBox

To add the UI to the left side of the Blueprint node, we add the horizontal bar and the text to the member variable LeftNodeBox.
To add the UI to the right side of the Blueprint node, we need to manipulate the member variable RightNodeBox.
Thus, if you want to change the UI for a Blueprint node, you need to manipulate the member variable LeftNodeBox or RightNodeBox.

4. Associate the UI with the Blueprint Node

Finally, associate a Blueprint node (class UK2Node_MyManualSwitch) with the UI defined in class SGraphNodeK2Base.
This process can be done by adding the corresponding process to the member function FMyManualSwitchModule::StartupModule, which is executed on the module loading phase.

void FMyManualSwitchModule::StartupModule()
{
    GraphPanelNodeFactory_MyManualSwitch = MakeShareable(new FGraphPanelNodeFactory_MyManualSwitch());
    FEdGraphUtilities::RegisterVisualNodeFactory(GraphPanelNodeFactory_MyManualSwitch);
}

This time, we define the class FGraphPanelNodeFactory_MyManualSwitch and pass its instance to the member function FEdGraphUtilities::RegisterVisualNodeFactory.
The definition of class FGraphPanelNodeFactory_MyManualSwitch is shown below.

class FGraphPanelNodeFactory_MyManualSwitch : public FGraphPanelNodeFactory
{
    virtual TSharedPtr CreateNode(UEdGraphNode* Node) const override
    {
        if (UK2Node_MyManualSwitch* MyManualSwitch = Cast(Node))
        {
            return SNew(SGraphNodeMyManualSwitch, MyManualSwitch);
        }
        return nullptr;
    }
};

Inherits class FGraphPanelNodeFactory and overrides member function CreateNode.
The member function CreateNode has a argument that is the Blueprint node inherited from class UEdGraphNode.
So, we need to check if the blueprint node is a UK2Node_MyManualSwitch or not.
In it is UK2Node_MyManualSwitch, we call SNew to instantiate SGraphNodeMyManualSwitch.
We can find the customized UI will be displayed when a user add UK2Node_MyManualSwitch which calls SGraphNodeMyManualSwitch.

The custom UI must be unassociated when the module is unloaded.

void FMyManualSwitchModule::ShutdownModule()
{
    if (GraphPanelNodeFactory_MyManualSwitch.IsValid())
    {
        FEdGraphUtilities::UnregisterVisualNodeFactory(GraphPanelNodeFactory_MyManualSwitch);
        GraphPanelNodeFactory_MyManualSwitch.Reset();
    }
}

Use the Created Node

Let’s use the node you created.
Open the Blueprint Editor, right-click to display the menu, and enter "mymaualswitch" in the search box.

Search for node "MyManualSwitch"

Select and place the node "MyManualSwitch" in the category "Flow Control".

Create Actor

If you right-click on a Blueprint node and change the execution destination, the text will be changed according to the current execution destination.