{"id":1137,"date":"2022-08-15T16:21:20","date_gmt":"2022-08-15T07:21:20","guid":{"rendered":"https:\/\/colory-games.net\/site\/?p=1137"},"modified":"2023-11-04T20:42:07","modified_gmt":"2023-11-04T11:42:07","slug":"create-your-own-blueprint-node-by-inheriting-k2node-4-ui-en","status":"publish","type":"post","link":"https:\/\/colory-games.net\/site\/en\/create-your-own-blueprint-node-by-inheriting-k2node-4-ui-en\/","title":{"rendered":"[UE5] Create Your Own Blueprint Node by Inheriting K2Node (4) UI"},"content":{"rendered":"\n<div class=\"wp-block-jetpack-markdown\"><p>Blueprint node has a clean UI by default.<br>\nHowever, some Blueprint nodes provide own UI that users can use easier than default.<br>\nFor example, the Blueprint node &quot;Switch on Int&quot; has a horizontal bar above the default execution pin.<br>\nThis makes it easier to distinguish from conditional execution pin.<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/switch_on_int.png?ssl=1\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/switch_on_int.png?ssl=1\" alt=\"Blueprint node &quot;Switch on Int&quot; UI\" data-recalc-dims=\"1\"><\/a><\/p>\n<p>In this article, we will modify the UI of the Blueprint node created in <a href=\"https:\/\/colory-games.net\/site\/en\/create-your-own-blueprint-node-by-inheriting-k2node-3-menu-en\/\">the Menu article<\/a>.<\/p>\n<ul>\n<li><a href=\"https:\/\/colory-games.net\/site\/en\/create-your-own-blueprint-node-by-inheriting-k2node-1-basic-en\/\">Create Your Own Blueprint Node by Inheriting K2Node (1) Basic<\/a><\/li>\n<li><a href=\"https:\/\/colory-games.net\/site\/en\/create-your-own-blueprint-node-by-inheriting-k2node-2-execution-pin-en\/\">Create Your Own Blueprint Node by Inheriting K2Node (2) Execution Pin<\/a><\/li>\n<li><a href=\"https:\/\/colory-games.net\/site\/en\/create-your-own-blueprint-node-by-inheriting-k2node-3-menu-en\/\">Create Your Own Blueprint Node by Inheriting K2Node (3) Menu<\/a><\/li>\n<li>Create Your Own Blueprint Node by Inheriting K2Node (4) UI &lt;&#8212; This article<\/li>\n<\/ul>\n<div id=\"toc_container\" class=\"no_bullets\"><p class=\"toc_title\">\u76ee\u6b21<\/p><ul class=\"toc_list\"><li><a href=\"#Source_Code\">Source Code<\/a><\/li><li><a href=\"#UI_Changes\">UI Changes<\/a><\/li><li><a href=\"#Create_a_Module\">Create a Module<\/a><\/li><li><a href=\"#Change_the_UI_of_Blueprint_Node\">Change the UI of Blueprint Node<\/a><ul><li><a href=\"#1_Inherit_from_Class_SGraphNodeK2Base\">1. Inherit from Class SGraphNodeK2Base<\/a><\/li><li><a href=\"#2_Configure_Basic_Information\">2. Configure Basic Information<\/a><\/li><li><a href=\"#3_Define_the_UI_Construction_Procedure\">3. Define the UI Construction Procedure<\/a><\/li><li><a href=\"#4_Associate_the_UI_with_the_Blueprint_Node\">4. Associate the UI with the Blueprint Node<\/a><\/li><\/ul><\/li><li><a href=\"#Use_the_Created_Node\">Use the Created Node<\/a><\/li><\/ul><\/div>\n<h1><span id=\"Source_Code\">Source Code<\/span><\/h1>\n<p>The complete source code is available on GitHub.<\/p>\n<p><a href=\"https:\/\/github.com\/colory-games\/techblog-sample-projects\/tree\/main\/nutti\/20220815\">https:\/\/github.com\/colory-games\/techblog-sample-projects\/tree\/main\/nutti\/20220815<\/a><\/p>\n<p>To run the source code, follow these steps.<\/p>\n<ol>\n<li>Place the directory <code>MyManualSwitch<\/code> into <code>Plugins<\/code> in your Unreal Engine project directory.<\/li>\n<li>Right click on the <code>.uproject<\/code> file and create a project file for Visual Studio.<\/li>\n<li>Open the project file and build the project.<\/li>\n<li>Run Unreal Engine from the <code>.uproject<\/code> file and activate the &quot;MyManualSwitch&quot; plugin.<\/li>\n<\/ol>\n<h1><span id=\"UI_Changes\">UI Changes<\/span><\/h1>\n<p>Here is the modified UI for the Blueprint node &quot;MyManualSwitch&quot;.<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/custom_ui.png?ssl=1\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/custom_ui.png?ssl=1\" alt=\"Blueprint node &quot;MyManualSwitch&quot; UI\" data-recalc-dims=\"1\"><\/a><\/p>\n<p>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.<br>\nNotice also that the text changes when the execution destination changes.<\/p>\n<h1><span id=\"Create_a_Module\">Create a Module<\/span><\/h1>\n<p>Create a module by following to the description in the Basic section.<br>\nFor the concreate instructions to create a module, please refer to the <a href=\"https:\/\/colory-games.net\/site\/en\/create-your-own-blueprint-node-by-inheriting-k2node-1-basic-en\/\">article for the basic<\/a>.<\/p>\n<h1><span id=\"Change_the_UI_of_Blueprint_Node\">Change the UI of Blueprint Node<\/span><\/h1>\n<p>To change the UI of a Blueprint node, follow the below steps.<\/p>\n<ol>\n<li>Inherit from class <code>SGraphNodeK2Base<\/code><\/li>\n<li>Configure basic information<\/li>\n<li>Define the UI construction procedure<\/li>\n<li>Associate the UI with the Blueprint node<\/li>\n<\/ol>\n<h2><span id=\"1_Inherit_from_Class_SGraphNodeK2Base\">1. Inherit from Class <code>SGraphNodeK2Base<\/code><\/span><\/h2>\n<p>First define the class <code>SGraphNodeMyManualSwitch<\/code>, which extends the class <code>SGraphNodeK2Base<\/code>.<\/p>\n<pre><code class=\"language-cpp\">class SGraphNodeMyManualSwitch : public SGraphNodeK2Base\n{\n    SLATE_BEGIN_ARGS(SGraphNodeMyManualSwitch)\n    {\n    }\n    SLATE_END_ARGS()\n    void Construct(const FArguments& InArgs, UK2Node_MyManualSwitch* InNode);\n    virtual void CreatePinWidgets() override;\n};\n<\/code><\/pre>\n<p>The macro <code>SLATE_BEGIN_ARGS<\/code> and <code>SLATE_END_ARGS<\/code> define the specification of parameters that can be passed at the instatiation time.<br>\nBut, we do not use them in this case.<\/p>\n<p>The class <code>SGraphNodeMyManualSwitch<\/code> has two member functions.<\/p>\n<table>\n<thead>\n<tr>\n<th><strong>Member Function Name<\/strong><\/th>\n<th><strong>Role<\/strong><\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>Construct<\/code><\/td>\n<td>Called at instantiation time.<br>Configure basic information here.<\/td>\n<\/tr>\n<tr>\n<td><code>CreatePinWidgets<\/code><\/td>\n<td>Called when the Engine draw input\/output pins.<br>The construction process of UI need to be defined here.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2><span id=\"2_Configure_Basic_Information\">2. Configure Basic Information<\/span><\/h2>\n<p>Basic information can be configured by defining a configuration process in the member function <code>Construct<\/code> of class <code>SGraphNodeMyManualSwitch<\/code>.<\/p>\n<pre><code class=\"language-cpp\">void SGraphNodeMyManualSwitch::Construct(const FArguments& InArgs, UK2Node_MyManualSwitch* InNode)\n{\n    GraphNode = InNode;\n    SetCursor(EMouseCursor::GrabHand);\n    UpdateGraphNode();\n}\n<\/code><\/pre>\n<p><code>GraphNode<\/code> is a member variable defined in the parent class and will be used later to define the construction process of the UI.<br>\nIt is common to assign the argument <code>InNode<\/code>.<\/p>\n<p>Next, call the member function <code>SetCursor<\/code> to change the cursor to a hand icon when the mouse over a Blueprint node.<br>\nNote that the cursor icon can be changed depending on the value of the enumerated type <code>EMouseCursor<\/code> passed to the member function <code>SetCursor<\/code>.<\/p>\n<p>Finally, call the member function <code>UpdateGraphNode<\/code> to notify that the UI of Blueprint node has been changed.<\/p>\n<h2><span id=\"3_Define_the_UI_Construction_Procedure\">3. Define the UI Construction Procedure<\/span><\/h2>\n<p>Define the construction process of the UI by overriding the member function <code>CreatePinWidgets<\/code> of class <code>SGraphNode<\/code>.<br>\nThe UI is constructed according to the Unreal Engine&#8217;s <a href=\"https:\/\/docs.unrealengine.com\/4.27\/ja\/ProgrammingAndScripting\/Slate\/\">Slate UI Framework<\/a>.<\/p>\n<pre><code class=\"language-cpp\">void SGraphNodeMyManualSwitch::CreatePinWidgets()\n{\n    UK2Node_MyManualSwitch* MyManualSwitch = CastChecked<UK2Node_MyManualSwitch>(GraphNode);\n    \/\/ Add pin UI.\n    for (auto It = GraphNode->Pins.CreateConstIterator(); It; ++It)\n    {\n        UEdGraphPin* Pin = *It;\n        if (!Pin->bHidden)\n        {\n            TSharedPtr<SGraphPin> NewPin = FNodeFactory::CreatePinWidget(Pin);\n            check(NewPin.IsValid());\n            AddPin(NewPin.ToSharedRef());\n        }\n    }\n    bool bExecuteA = MyManualSwitch->GetSwitchValue();\n    \/\/ Add horizontal bar.\n    LeftNodeBox->AddSlot()\n        .AutoHeight()\n        .HAlign(HAlign_Left)\n        .VAlign(VAlign_Center)\n        [\n            SNew(SImage)\n            .Image(FEditorStyle::GetBrush(&quot;Graph.Pin.DefaultPinSeparator&quot;))\n        ];\n    \/\/ Add text.\n    LeftNodeBox->AddSlot()\n        .AutoHeight()\n        .HAlign(HAlign_Left)\n        .VAlign(VAlign_Center)\n        .Padding(10.0f, 5.0f)\n        [\n            SNew(STextBlock)\n            .Text(FText::FromString(bExecuteA ? &quot;Execute A&quot; : &quot;Execute B&quot;))\n        ];\n}\n<\/code><\/pre>\n<p>We will not discuss the Slate UI framework in detail here, but explain two important things.<\/p>\n<h3>Add Pins<\/h3>\n<p>New pin can be created with the member function <code>CreatePinWidget<\/code> of class <code>FNodeFactory<\/code>.<br>\nThen, it can be added to the UI of a Blueprint node by calling the member function <code>AddPin<\/code>.<br>\nThese member functions are the easiest way to implement if you use the default UI.<br>\nThe member function <code>AddPin<\/code> automatically determines the location of the UI to be added based on the input\/output of the pin.<\/p>\n<h3>LeftNodeBox<\/h3>\n<p>To add the UI to the left side of the Blueprint node, we add the horizontal bar and the text to the member variable <code>LeftNodeBox<\/code>.<br>\nTo add the UI to the right side of the Blueprint node, we need to manipulate the member variable <code>RightNodeBox<\/code>.<br>\nThus, if you want to change the UI for a Blueprint node, you need to manipulate the member variable <code>LeftNodeBox<\/code> or <code>RightNodeBox<\/code>.<\/p>\n<h2><span id=\"4_Associate_the_UI_with_the_Blueprint_Node\">4. Associate the UI with the Blueprint Node<\/span><\/h2>\n<p>Finally, associate a Blueprint node (class <code>UK2Node_MyManualSwitch<\/code>) with the UI defined in class <code>SGraphNodeK2Base<\/code>.<br>\nThis process can be done by adding the corresponding process to the member function <code>FMyManualSwitchModule::StartupModule<\/code>, which is executed on the module loading phase.<\/p>\n<pre><code class=\"language-cpp\">void FMyManualSwitchModule::StartupModule()\n{\n    GraphPanelNodeFactory_MyManualSwitch = MakeShareable(new FGraphPanelNodeFactory_MyManualSwitch());\n    FEdGraphUtilities::RegisterVisualNodeFactory(GraphPanelNodeFactory_MyManualSwitch);\n}\n<\/code><\/pre>\n<p>This time, we define the class <code>FGraphPanelNodeFactory_MyManualSwitch<\/code> and pass its instance to the member function <code>FEdGraphUtilities::RegisterVisualNodeFactory<\/code>.<br>\nThe definition of class <code>FGraphPanelNodeFactory_MyManualSwitch<\/code> is shown below.<\/p>\n<pre><code class=\"language-cpp\">class FGraphPanelNodeFactory_MyManualSwitch : public FGraphPanelNodeFactory\n{\n    virtual TSharedPtr<SGraphNode> CreateNode(UEdGraphNode* Node) const override\n    {\n        if (UK2Node_MyManualSwitch* MyManualSwitch = Cast<UK2Node_MyManualSwitch>(Node))\n        {\n            return SNew(SGraphNodeMyManualSwitch, MyManualSwitch);\n        }\n        return nullptr;\n    }\n};\n<\/code><\/pre>\n<p>Inherits class <code>FGraphPanelNodeFactory<\/code> and overrides member function <code>CreateNode<\/code>.<br>\nThe member function <code>CreateNode<\/code> has a argument that is the Blueprint node inherited from class <code>UEdGraphNode<\/code>.<br>\nSo, we need to check if the blueprint node is a <code>UK2Node_MyManualSwitch<\/code> or not.<br>\nIn it is <code>UK2Node_MyManualSwitch<\/code>, we call <code>SNew<\/code> to instantiate <code>SGraphNodeMyManualSwitch<\/code>.<br>\nWe can find the customized UI will be displayed when a user add <code>UK2Node_MyManualSwitch<\/code> which calls <code>SGraphNodeMyManualSwitch<\/code>.<\/p>\n<p>The custom UI must be unassociated when the module is unloaded.<\/p>\n<pre><code class=\"language-cpp\">void FMyManualSwitchModule::ShutdownModule()\n{\n    if (GraphPanelNodeFactory_MyManualSwitch.IsValid())\n    {\n        FEdGraphUtilities::UnregisterVisualNodeFactory(GraphPanelNodeFactory_MyManualSwitch);\n        GraphPanelNodeFactory_MyManualSwitch.Reset();\n    }\n}\n<\/code><\/pre>\n<h1><span id=\"Use_the_Created_Node\">Use the Created Node<\/span><\/h1>\n<p>Let&#8217;s use the node you created.<br>\nOpen the Blueprint Editor, right-click to display the menu, and enter &quot;mymaualswitch&quot; in the search box.<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/use_custom_node_search.png?ssl=1\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/use_custom_node_search.png?ssl=1\" alt=\"Search for node &quot;MyManualSwitch&quot;\" data-recalc-dims=\"1\"><\/a><\/p>\n<p>Select and place the node &quot;MyManualSwitch&quot; in the category &quot;Flow Control&quot;.<\/p>\n<p><a href=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/use_custom_node_build_actor.png?ssl=1\"><img decoding=\"async\" src=\"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/use_custom_node_build_actor.png?ssl=1\" alt=\"Create Actor\" data-recalc-dims=\"1\"><\/a><\/p>\n<p>If you right-click on a Blueprint node and change the execution destination, the text will be changed according to the current execution destination.<\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":2,"featured_media":957,"comment_status":"open","ping_status":"open","sticky":false,"template":"templates\/single-home-techblog.php","format":"standard","meta":{"footnotes":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false,"jetpack_publicize_feature_enabled":false,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","enabled":false}}},"categories":[154,71],"tags":[177],"jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"https:\/\/i0.wp.com\/colory-games.net\/site\/wp-content\/uploads\/tech-blog\/nutti\/2022\/08\/20220815\/custom_ui.png?fit=380%2C261&ssl=1","_links":{"self":[{"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/posts\/1137"}],"collection":[{"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/comments?post=1137"}],"version-history":[{"count":1,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/posts\/1137\/revisions"}],"predecessor-version":[{"id":1138,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/posts\/1137\/revisions\/1138"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/media\/957"}],"wp:attachment":[{"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/media?parent=1137"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/categories?post=1137"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/colory-games.net\/site\/wp-json\/wp\/v2\/tags?post=1137"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}