This article explains how to create a blueprint node with an execution pin.
The explanation assumes that you are familiar with the basic contents of creating the Blueprint node.
Please check the Basic article if necessary.
- Create Your Own Blueprint Node by Inheriting K2Node (1) Basic
- Create Your Own Blueprint Node by Inheriting K2Node (2) Execution Pin <— This article
- 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/20220626
To run the source code, follow these steps.
- Place the directory
MyCompareInt
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 "MyCompareInt" plugin.
Specification of the Blueprint Node
The specification of the Blueprint node "MyCompareInt" is as follows.
- Receives two integers "A" and "B" as inputs.
- Changes the output execution pin to be executed depending on the inputs "A" and "B".
- Execution pin "Greater": Execute when A>B
- Execution pin "Equal": Execute when A=B
- Execution pin "Less": Execute when A<B
This specification is similar to the macro "Compare Int" provided in the Blueprint.
Please check the implementation of the macro "Compare Int" if necessary.
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.
Create a Blueprint Node
We can create a Blueprint node by the following steps.
- Inherit class
UK2Node
. - Setup basic information.
- Add pins.
- Add internal pins.
- Define the process of execution pins.
Steps 1 and 2 are the almost same as in the article for the basic, so we will start from step 3.
3. Add Pins
Add the following pins according to the specification.
Pin Name | Input/Output | Role |
---|---|---|
Input | Execute the node. | |
A | Input | Input of integer. |
B | Input | Input of integer. |
Greater | Output | Execute when A>B |
Equal | Output | Execute when A=B |
Less | Output | Execute when A<B |
We need to add those pins.
AllocateDefaultPins
is the member funciton which defines the process of adding pins to the node.
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, APinName);
CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Int, BPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, GreaterPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, EqualPinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, LessPinName);
To add an execution pin, specify UEdGraphSchema_K2::PC_Exec
for the second argument.
If you want to add execution pin on the input side that triggers node execution, the third argument needs to be UEdGraphSchema_K2::PN_Execute
.
Add three execution pins on the output side as well.
We add execution pins whose names are Greater, Equal, and Less.
4. Add Internal Pins
The node "MyCompareInt" needs to determine the pin on the output side to be executed according to the value of the input integers "A" and "B".
- When "A>B", execute the pin "Greater".
- When "A=B", execute the pin "Equal".
- When "A<B", execute the pin "Less".
We can implement this in Blueprint as follows.
However, we will implement as follows.
- Pass the inputs ‘A’ and ‘B’ to the library function
UKismetMathLibrary::GreaterEqual_IntInt
. If the result isFalse
, execute the node connected to the execution pin ‘Less’. - If the result of 1 is
True
, pass the inputs ‘A’ and ‘B’ to the library functionUKismetMathLibrary::LessEqual_IntInt
. If the result isFalse
, execute the node connected to the execution pin ‘Greater’. - If the result of 2 is
True
, execute the node connected to the execution pinEqual
.
To realize in Blueprint, you can implement this as follows.
To achieve, you need to understand the following two things.
- Add an internal pin, which is required to call the library function.
- Determine the execution pin to execute by using the output result of the added library function.
First, we will desribe the process of adding the internal pins required for a library function call.
Use the member function CreatePin
is used to add the internal pins.
UEdGraphPin* FunctionPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Object, UKismetMathLibrary::StaticClass(), TEXT("GreaterEqual_IntInt"));
FunctionPin->bDefaultValueIsReadOnly = true;
FunctionPin->bNotConnectable = true;
FunctionPin->bHidden = true;
We specify the second argument of the UEdGraphSchema_K2::PC_Object
.
We specify the blueprint library function to the third argument, and specify the name of the library function to the fourth argument.
Use the return value of the library function CreatePin
and apply various configuration to make the internal pin read-only.
The pin to execute the library function has been created, but there is no library function to execute.
The process for setting the library function is shown below.
UFunction* Function = FindUField(UKismetMathLibrary::StaticClass(), TEXT("GreaterEqual_IntInt"));
if (Function != nullptr && Function->HasAllFunctionFlags(FUNC_Static))
{
UBlueprint* Blueprint = GetBlueprint();
if (Blueprint != nullptr)
{
UClass* FunctionOwnerClass = Function->GetOuterUClass();
if (!Blueprint->SkeletonGeneratedClass->IsChildOf(FunctionOwnerClass))
{
FunctionPin->DefaultObject = FunctionOwnerClass->GetDefaultObject();
}
}
}
Call the function FindUField
to get the UFunction
of the library function UKismetMathLibrary::GreaterEqual_IntInt
.
Then, set UFunction
to the member variable DefaultObject
of the created inner pin FunctionPin
.
This allows the inner pin to execute the library function UKismetMathLibrary::GreaterEqual_IntInt
.
In the same way, you can create an inner pin to execute the library function UKismetMathLibrary::LessEqual_IntInt
.
5. Define the Process of Execution Pins
We will implement a process that uses the output result of a library function to determine the execution pin to be executed.
We defined a computation graph by using the member function ExpandNode
.
However, the member function ExpandNode
cannot realize the process of selecting a pin to execute based on a specific condition.
To realize this, you need to define a class FKCHandler_MyCompareInt
inherited from class FNodeHandlingFunctor
.
class FKCHandler_MyCompareInt : public FNodeHandlingFunctor
{
TMap BoolTermMap;
public:
FKCHandler_MyCompareInt(FKismetCompilerContext& InCompilerContext) : FNodeHandlingFunctor(InCompilerContext)
{
}
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
// snip
}
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
// snip
}
};
Create Variables
The member function RegisterNets
defines the process of creating temporary variables to store the results of the library functions you have executed.
The temporary variables will be used later.
virtual void RegisterNets(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
FNodeHandlingFunctor::RegisterNets(Context, Node);
{
// Store the result of library function "UKismetMathLibrary::GreaterEqual_IntInt"
// to the temporary boolean variable.
FBPTerminal* BoolTerm = Context.CreateLocalTerminal();
BoolTerm->Type.PinCategory = UEdGraphSchema_K2::PC_Boolean;
BoolTerm->Source = Node;
BoolTerm->Name = Context.NetNameMap->MakeValidName(Node, TEXT("IsGreaterEqual"));
BoolTermMap.Add(TEXT("IsGreaterEqual"), BoolTerm);
}
}
The member function RegisterNets
must first call RegisterNets
of the parent class.
After calling RegisterNets
of the parent class, create a boolean variable by calling the member function CreateLocalTerminal
with the argument Context
.
We repeat this process twice, to create the variables IsGreaterEqual
and IsLessEqual
for storing the results of UKismetMathLibrary::LessEqual_IntInt
.
The created variables are registered in the member variable BoolTermMap
to be used while the Blueprint compilation.
Blueprint Compilation
The member function Compile
defines the process to be called while the Blueprint compilation.
This function will add a Blueprint statement FKismetCompiledStatement
.
The member function Compile
defined here can be called in the Compile Functions phase.
The concreate process of Blueprint compilation is described in the official documentation Blueprint Compiler Overview.
The Blueprint statement type can be found in the EKismetCompiledStatementType.
We will use the following statement types.
Statement Type | Meaning |
---|---|
KCST_CallFunction |
Calls a function (UFunction ) |
KCST_GotoIfNot |
Transfer the control of execution to a specific execution pin if the specified condition is False |
KCST_UnconditionalGoto |
Unconditionally transfer the control of execution to a specific execution pin |
A statement can be created by calling the member function AppendStatementForNode
with the argument Context
.
The return value is the structure FBlueprintCompiledStatement
.
The member variable Type
is a statement type, and LHS
and RHS
are variables to be passed to the statement.
The meaning of the variables depends on the statement type, see EKismetCompiledStatementType for detail.
Get Required Variables for the Compilation
The member function Compile
first retrieves variables needed at compilation time.
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
UK2Node_MyCompareInt* MyCompareIntNode = CastChecked(Node);
// Get execution pin on the output side.
UEdGraphPin* GreaterPin = MyCompareIntNode->FindPin(GreaterPinName);
UEdGraphPin* EqualPin = MyCompareIntNode->FindPin(EqualPinName);
UEdGraphPin* LessPin = MyCompareIntNode->FindPin(LessPinName);
// Get temporary variables.
FBPTerminal* IsGreaterEqualTerm = BoolTermMap.FindRef(TEXT("IsGreaterEqual"));
FBPTerminal* IsLessEqualTerm = BoolTermMap.FindRef(TEXT("IsLessEqual"));
// Get variables of input pin "A".
UEdGraphPin* APin = MyCompareIntNode->FindPin(APinName);
UEdGraphPin* ANet = FEdGraphUtilities::GetNetFromPin(APin);
FBPTerminal* ATerm = Context.NetMap.FindRef(ANet);
// Get variables of input pin "B".
UEdGraphPin* BPin = MyCompareIntNode->FindPin(BPinName);
UEdGraphPin* BNet = FEdGraphUtilities::GetNetFromPin(BPin);
FBPTerminal* BTerm = Context.NetMap.FindRef(BNet);
}
FBPTerminal
is the equivalent of a variable in the statement.
Note that class UEdGraphPin
does not represent the execution pin.
We will get the following variables.
Variable Name | Meaning |
---|---|
IsGreaterEqualTerm |
Store the result of the library function UKismetMathLibrary::GreaterEqual_IntInt |
IsLessEqualTerm |
Store the result of library function UKismetMathLibrary::LessEqual_IntInt |
ATerm |
Input pin "A" |
BTerm |
input pin "B" |
Create Blueprint Statement
We will create Blueprint statements.
As we mentioned earlier, there is no statement type that transfers execution control to a specific execution pin if the specified condition is True
.
In step 4, we changed the method of implementation to take into account this constraint.
- Pass the inputs "A" and "B" to the library function
UKismetMathLibrary::GreaterEqual_IntInt
. If the result isFalse
, execute the node connected to the execution pin "Less". - If the result of 1 is
True
, pass the inputs "A" and "B" to the library functionUKismetMathLibrary::LessEqual_IntInt
. If the result isFalse
, execute the node connected to the execution pin "Greater". - If the result of 2 is
True
, execute the node connected to the execution pin "Equal".
We will show the pseudo code for a C++ implementation.
IsGreaterEqualTerm = UKismetMathLibrary::GreaterEqual_IntInt(ATerm, BTerm);
if (!IsGreaterEqualTerm) {
goto LessPin;
}
IsLessEqualTerm = UKismetMathLibrary::LessEqual_IntInt(ATerm, BTerm);
if (!IsLessEqualTerm) {
goto GreaterPin;
}
goto EqualPin;
This can be rewritten into a blueprint statement as follows.
KCST_CallFunction IsGreaterEqualTerm [ATerm, BTerm]
KCST_GotoIfNot IsGreaterEqualTerm -> LessPin
KCST_CallFunction IsLessEqualTerm [ATerm, BTerm]
KCST_GotoIfNot IsLessEqualTerm -> GreaterPin
KCST_UnconditionalGoto -> EqualPin
We will create these statements in the member function Compile
.
virtual void Compile(FKismetFunctionContext& Context, UEdGraphNode* Node) override
{
// snip
{
// Get UFunction of the library function "UKismetMathLibrary::GreaterEqual_IntInt".
UEdGraphPin* FunctionPin = MyCompareIntNode->FindPin(TEXT("GreaterEqual_IntInt"));
FBPTerminal* FunctionContext = Context.NetMap.FindRef(FunctionPin);
UClass* FunctionClass = Cast(FunctionPin->PinType.PinSubCategoryObject.Get());
UFunction* FunctionPtr = FindUField(FunctionClass, FunctionPin->PinName);
// Create a statement that calls a library function.
FBlueprintCompiledStatement& CallFuncStatement = Context.AppendStatementForNode(MyCompareIntNode);
CallFuncStatement.Type = KCST_CallFunction;
CallFuncStatement.FunctionToCall = FunctionPtr;
CallFuncStatement.FunctionContext = FunctionContext;
CallFuncStatement.bIsParentContext = false;
CallFuncStatement.LHS = IsGreaterEqualTerm;
CallFuncStatement.RHS.Add(ATerm);
CallFuncStatement.RHS.Add(BTerm);
// Create a statement that transfers execution control to the execution pin "LessPin" when the result of a library function call is false.
FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(MyCompareIntNode);
GotoStatement.Type = KCST_GotoIfNot;
GotoStatement.LHS = IsGreaterEqualTerm;
Context.GotoFixupRequestMap.Add(&GotoStatement, LessPin);
}
{
// snip
// Create a statement that transfers execution control to the execution pin "GreaterPin" when the result of a library function call is false.
FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(MyCompareIntNode);
GotoStatement.Type = KCST_GotoIfNot;
GotoStatement.LHS = IsLessEqualTerm;
Context.GotoFixupRequestMap.Add(&GotoStatement, GreaterPin);
}
// If none of the conditions are met, create a statement that unconditionally transfers execution control to the execution pin "EqualPin".
GenerateSimpleThenGoto(Context, *MyCompareIntNode, EqualPin);
}
First, get the UFunction
of the library function UKismetMathLibrary::GreaterEqual_IntInt
to be used for creating Blueprint statements.
It can be obtained from the internal pin created in step 4.
Next, call the member function AppendStatementForNode
of Context
to create a statement KCST_CallFunction
that calls the library function.
Set the member variable Type
of the structure FBlueprintCompiledStatement
to KCST_CallFunction
.
This indicates that this statement calls a library function.
The member variable FunctionToCall
is set to the UFunction
of the library function to be called in this statement.
Set the member variable LHS
to the variable IsGreaterEqualTerm
that stores the return value of the library function.
Set the member variable RHS
to the values to be passed as the argument of the library function in the correct order.
In this case, ATerm
and BTerm
are arguments.
Then, we will create the statement KCST_GotoIfNot
, which transfers execution control to a specific execution pin if the specified condition is False
.
Specify IsGreaterEqualTerm
to the member variable LHS
so that execution control is transferred when IsGreaterEqualTerm
is False
("A<B").
Since the destination of the execution control is the execution pin LessPin
, call Context.GotoFixupRequestMap.Add
to register the destination of the execution control.
In the same way, create a statement that transfers control of execution to the execution pin GreaterPin
.
Finally, the function GenerateSimpleThenGoto
is called to create a statement that transfers control of execution to the execution pin EqualPin
if none of the conditions are met ("A=B").
The function GenerateSimpleThenGoto
is a convenience function that internally creates the statement KCST_UnconditionalGoto
and calls Context.GotoFixupRequestMap.Add
to register the pin to which execution control is transferred.
Register the Function Executed on the Execution Pin
The class FKCHandler_MyCompareInt
, which defines the process of the execution pin, must be associated with the class UK2Node_MyCompareInt
of the node.
This can be done by overriding the member function CreateNodeHandler
which returns the class FKCHandler_MyCompareInt
.
FNodeHandlingFunctor* UK2Node_MyCompareInt::CreateNodeHandler(class FKismetCompilerContext& CompilerContext) const
{
return new FKCHandler_MyCompareInt(CompilerContext);
}
Use the Created Node
Let’s use the node you created.
Open the Blueprint Editor, right-click to display the menu, and enter "mycompareint" in the search box.
Select and place the node "MyComponentInt" in the category "Flow Control".
When the created Actor is placed in the level, we can see that the execution result will be changed depending on the input values of the node.
Here is the execution pin "Greater".
You can try to change the values of input pins "A" and "B".