Unreal Engineで独自のブループリントノードを作る場合、すぐに思いつくものとしてはライブラリ関数を作るというものです。
しかしライブラリ関数で作れるブループリントノードには限界があり、たとえば「Switch on Int」のような実行制御を伴う複雑なノードを作ることはできません。
本記事では、クラスUK2Nodeを継承してブループリントノードを作る方法について、具体的にブループリントノードを作りながら説明します。
ブループリントノードの作り方自体は深い内容であるため、トピックに分けて説明します。
- K2Nodeを継承してブループリントノードを自作する(1) 基本編 ★本記事
- K2Nodeを継承してブループリントノードを自作する(2) 実行ピン編
- K2Nodeを継承してブループリントノードを自作する(3) メニュー編
- K2Nodeを継承してブループリントノードを自作する(4) UI編
ソースコード一式
本記事で説明するソースコード一式は、GitHubで公開しています。
https://github.com/colory-games/techblog-sample-projects/tree/main/nutti/20220622
ソースコードを利用して動作確認する場合は、次の手順に従ってください。
- ディレクトリ
MyDivide
を、Unreal EngineのプロジェクトディレクトリにあるPlugins
に配置する .uproject
ファイルを右クリックし、Visual Studio向けのプロジェクトファイルを作成する- プロジェクトファイルを開き、プロジェクトをビルドする
.uproject
ファイルからUnreal Engineを起動し、プラグイン「MyDivide」を有効化する
作成するブループリントノードの仕様
本記事で作成するブループリントノード「MyDivide」の仕様を次に示します。
- 入力として2つの整数「A」「B」を受け取り、A÷Bの商「Quotient」と余り「Remainder」を出力する
- 実行ピンなし(Pure関数)
今回作成するノードは、ブループリントエディタを使って作成可能なノードです。
しかしここではあえて、クラスUK2Nodeを利用して作成することにします。
モジュールの作成
ここでは、ブループリントノード「MyDivide」をプラグイン形式で提供することを考えたソースコード構成とします。
最初に Build.cs
ファイルの内容を示します。
ブループリントノードを作成するために必要なモジュールが設定されています。
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"
});
}
}
また、.uplugin
ファイルの Modules
に次のエントリを追加します。
{
"Name": "MyDivide",
"Type": "UncookedOnly",
"LoadingPhase": "PreLoadingScreen"
}
ブループリントノードの作成
ブループリントノードは、次の手順で作成します。
- クラス
UK2Node
の継承 - 基本情報の設定
- ピンの追加
- Pure関数化
- データピンの処理を定義
ブループリントノードは、クラス UK2Node
を継承して定義します。
ヘッダファイル K2Node_MyDivide.h
を作成し、クラス UK2Node_MyDivide
を定義します。
UCLASS(meta = (Keywords = "MyDivide Divide"))
class UK2Node_MyDivide : public UK2Node
{
GENERATED_BODY()
// クラス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;
// クラス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;
};
クラス UK2Node_MyDivide
には、次に示すメンバ関数が宣言されています。
メンバ関数 | 役割 |
---|---|
AllocateDefaultPins |
デフォルトのピンを割り当てる |
GetTooltipText |
Tooltipのテキストを設定する |
GetNodeTitleColor |
タイトル部の色を設定する |
GetNodeTitle |
タイトル部に表示するテキストを設定する |
GetIconAndTint |
タイトル部に表示するアイコンを設定する |
IsNodePure |
true を返した場合は、Pure関数化する |
GetMenuActions |
ブループリントエディタからノードを利用可能にする |
GetMenuCategory |
ノード検索メニューのカテゴリを設定する |
ExpandNode |
コンパイル時にノードを追加/削除する |
宣言したメンバ関数の定義を作成します。
ソースファイル K2Node_MyDivide.cpp
を作成し、各メンバ関数を定義します。
処理の詳細は、各関数内のコメントを参考にしてください。
FText UK2Node_MyDivide::GetTooltipText() const
{
// Tooltipで表示するテキストを返す
return LOCTEXT("MyDivide_Tooltip", "MyDivide\nGet quotient and remainder from dividing two integer");
}
FLinearColor UK2Node_MyDivide::GetNodeTitleColor() const
{
// 整数型(Integer)のピンと同じ色をタイトル部の色とする
return GetDefault()->IntPinTypeColor;
}
FText UK2Node_MyDivide::GetNodeTitle(ENodeTitleType::Type TitleType) const
{
// タイトル部に表示するテキストを返す
return LOCTEXT("MyDivide", "MyDivide");
}
FSlateIcon UK2Node_MyDivide::GetIconAndTint(FLinearColor& OutColor) const
{
// 関数アイコンをタイトル部に表示するアイコンとする
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
{
// カテゴリ「Math」に登録する
return FEditorCategoryUtils::GetCommonCategory(FCommonEditorCategory::Math);
}
メンバ関数 GetMenuActions
には、ブループリントエディタ上で右クリックしたメニューにノード「MyDivide」を追加する処理です。
メンバ関数 GetMenuActions
の実装を忘れると、メニューにノード「MyDivide」が表示されず配置できないため注意が必要です。
なおブループリントノードを作る場合は同じ処理で対応可能なため、そのまま実装を流用して問題ありません。
また、メンバ関数 GetMenuCategory
を呼び出すことで、カテゴリ「Math」にノード「MyDivide」が登録されます。
他のメンバ関数の役割については、ブループリントエディタに完成版のノード「MyDivide」を配置したときのUIと関連付けて理解するとよいでしょう。
作成するノードの仕様に従って、次のピンを追加します。
ピン名 | 入出力方向 | 意味 |
---|---|---|
A | 入力 | 整数の入力 |
B | 入力 | 整数の入力 |
Quotient | 出力 | A÷Bの商 |
Remainder | 出力 | A÷Bの余り |
今回作成するノードでは、上記のピンが追加されている必要があります。
メンバ関数 AllocateDefaultPins
は、ノード配置時にピンを追加する処理を定義するメンバ関数です。
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);
}
ピンは、メンバ関数 CreatePin
を呼び出して追加できます。
第1引数にピンの入出力方向を指定します。
入力の場合は EGPD_Input
、出力の場合は EGPD_Output
を指定します。
第2引数にはピンの型を指定します。
すべてのピンは整数型(Integer)であるため、UEdGraphSchema_K2::PC_Int
を指定します。
第3引数にはピン名を指定します。
今回作成するノードは実行ピンを持たないPure関数化されたノードであるため、メンバ関数 IsNodePure
が true
を返すようにします。
bool UK2Node_MyDivide::IsNodePure() const
{
return true;
}
最後にデータピンの処理を定義します。
データピンの処理は、計算グラフとして定義する必要があります。
今回作成するノードで実行する処理は、入力として2つの整数「A」「B」を受け取り、A÷Bの商「Quotient」と余り「Remainder」を出力するというものでした。
これをブループリントで実現する場合は、次のようにします。
- 入力ピン「A」と「B」を「/」ノードの入力に接続し、その出力を出力ピン「Quotient」に接続する
- 入力ピン「A」と「B」を「%」ノードの入力に接続し、その出力を出力ピン「Remainder」に接続する
「/」ノードは、C++のブループリント関数ライブラリ「UKismetMathLibrary」のライブラリ関数「Divide_IntInt」の呼び出しに対応します。
「%」ノードは、ブループリント関数ライブラリ「UKismetMathLibrary」のライブラリ関数「Percent_IntInt」の呼び出しに対応します。
したがってメンバ関数 ExpandNode
には、次の処理を実現するための処理を定義すればよいことになります。
- 入力ピン「A」と「B」を入力としてライブラリ関数「Divide_IntInt」を呼び出し、その結果を出力ピン「Quotient」に接続する
- 入力ピン「A」と「B」を入力としてライブラリ関数「Percent_IntInt」を呼び出し、その結果を出力ピン「Remainder」に接続する
メンバ関数 ExpandNode
のソースコードを次に示します。
void UK2Node_MyDivide::ExpandNode(FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph)
{
// 「/」ノードの配置
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")));
// 「%」ノードの配置
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")));
}
引数の CompilerContext
のメンバ関数 SpawnIntermediateNode
を呼び出し、テンプレートパラメータに UK2Node_CallFunction
を指定することで、ライブラリ関数を呼び出すためのノードを追加できます。
UK2Node_CallFunction
のメンバ関数 SetFromFunction
は、呼び出すライブラリ関数を指定するためのものです。
指定された引数より、追加された1つ目のノード(CallDivideFunction
)が「/」ノードを追加するためのものであることがわかります。
ただし、この状態では「/」ノードのピンが追加されていないため、メンバ関数 AllocateDefaultPins
を呼び出してピンを追加します。
CompilerContext
のメンバ関数 MovePinLinksToIntermediate
は、指定された2つのピンを接続します。
ここでは、次のピン同士を接続します。
- 出力ピン「Quotient」と
CallDivideFunction
の出力ピン - 入力ピン「A」と
CallDivideFunction
の入力ピン「A」 - 入力ピン「B」と
CallDivideFunction
の入力ピン「B」
同様にして「%」ノードを追加します。
これでノードを作成できたので、ビルドできることを確認します。
作成したノードの利用
作成したノードを利用してみましょう。 ブループリントエディタを開いて右クリックでメニューを表示し、検索ボックスに「mydivide」を入力します。
カテゴリ「Math」に追加されているノード「MyDivide」を選択して配置します。
入力の値を適当に設定したActorを作成します。
作成したActorをレベルに配置して実行すると、ノードの入力値を使って割り算で求めた商と余りが出力されることが確認できます。