アンリアる! C++入門編 ~対話形式で学ぶUnreal Engine~
BOOTHでUnreal Engine C++入門書を販売していますUnreal Engine上でC++を使って開発するために必要となる基礎知識を一冊の本にまとめた本です。 対話形式によるわかりやすい説明を目指しました。無料の試し読みも可能ですので、ぜひ読んでみてください!
[UE5] K2Nodeを継承してブループリントノードを自作する(3) メニュー編

本記事では、ブループリントノードを右クリックしたときのメニューを追加する方法を説明します。
具体的には、ノード「Sequence」を右クリックしたときに表示されるピン追加/削除のメニューが、ノードを右クリックしたときのメニューの一例です。

ノード「Sequence」のメニュー

なお、基本編の記事実行ピン編の記事 を理解していることを前提に説明します。

ソースコード一式

本記事で説明するソースコード一式は、GitHubで公開しています。

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

ソースコードを利用して動作確認する場合は、次の手順に従ってください。

  1. ディレクトリ MyManualSwitch を、Unreal Engineのプロジェクトディレクトリにある Plugins に配置する
  2. .uproject ファイルを右クリックし、Visual Studio向けのプロジェクトファイルを作成する
  3. プロジェクトファイルを開き、プロジェクトをビルドする
  4. .uproject ファイルからUnreal Engineを起動し、プラグイン「MyManualSwitch」を有効化する

作成するブループリントノードの仕様

本記事で作成するブループリントノード「MyManualSwitch」の仕様を次に示します。

  • 出力側に実行ピン「A」「B」を追加する
  • ノードを右クリックしたときにメニューを追加する
    • 「Execute A」:実行ピン「A」を実行するように設定する
    • 「Execute B」:実行ピン「B」を実行するように設定する

モジュールの作成

基本編で説明した内容に従ってモジュールを作成します。
具体的な作成方法については、基本編の記事 を参考にしてください。

ブループリントノードの作成

ブループリントノードは、次の手順で作成します。

  1. クラス UK2Node の継承
  2. 基本情報の設定
  3. ピンの追加
  4. 実行ピンの処理定義
  5. メニューを追加

なお、手順1と手順2については 基本編の記事 とほぼ同じであるため、ここでは手順3から説明します。

3. ピンの追加

作成するノードの仕様を満たすように、次のピンを追加します。

ピン名 入出力方向 意味
入力 ノードの実行を起動
A 出力 内部ピン「Switch」が True のときにピンの先につながったノードを実行
B 出力 内部ピン「Switch」が False のときにピンの先につながったノードを実行
Switch (内部) True のときに実行ピン「A」の先につながったノードを実行

ピンの追加処理は、メンバ関数 AllocateDefaultPins に実装します。

CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Exec, UEdGraphSchema_K2::PN_Execute);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, APinName);
CreatePin(EGPD_Output, UEdGraphSchema_K2::PC_Exec, BPinName);

// 内部ピンの作成
UEdGraphPin* SwitchPin = CreatePin(EGPD_Input, UEdGraphSchema_K2::PC_Boolean, SwitchPinName);
SwitchPin->DefaultValue = "true";
SwitchPin->bHidden = true;
SwitchPin->bDefaultValueIsReadOnly = true;
SwitchPin->bNotConnectable = true;

入出力側のピンを追加する処理については、基本編の記事実行ピン編の記事 を参考にしてください。

ここでは、実行する実行ピンを切り替えるフラグを保持する内部ピン「Switch」を追加していることに注意します。
内部ピンにするための設定については、基本的に 実行ピン編の記事 と同様です。
ただしメンバ変数 DefaultValue に値 "true" を指定することで、ピンのデフォルト値を true に設定しています。

4. 実行ピンの処理定義

実行ピンの処理を定義します。
今回作成するノード「MyManualSwitch」は、内部ピン「Switch」が True のときに実行ピン「A」の先につながったノードを実行し、False のときに実行ピン「B」の先につながったノードを実行します。
この動作を実現するため、実行ピン編の記事 で説明したブループリントのコンパイル処理を定義します。

メンバ関数 Compile に定義した、ブループリントコンパイル時の処理を示します。

UK2Node_MyManualSwitch* MyManualSwitchNode = CastChecked(Node);

UEdGraphPin* APin = MyManualSwitchNode->FindPin(APinName);
UEdGraphPin* BPin = MyManualSwitchNode->FindPin(BPinName);

// 内部ピン「Switch」の変数を取得する
UEdGraphPin* SwitchPin = MyManualSwitchNode->FindPin(SwitchPinName);
UEdGraphPin* SwitchNet = FEdGraphUtilities::GetNetFromPin(SwitchPin);
FBPTerminal* Switch = Context.NetMap.FindRef(SwitchNet);

// 内部ピン「Switch」の値がFalseのときに、実行ピン「B」に実行制御が移る
FBlueprintCompiledStatement& GotoStatement = Context.AppendStatementForNode(MyManualSwitchNode);
GotoStatement.Type = KCST_GotoIfNot;
GotoStatement.LHS = Switch;
Context.GotoFixupRequestMap.Add(&GotoStatement, BPin);

// 内部ピン「Switch」の値がTrueのときに、実行ピン「A」に実行制御が移る
GenerateSimpleThenGoto(Context, *MyManualSwitchNode, APin);

定義された処理は比較的単純です。
ステートメントタイプ KCST_GotoIfNot のステートメントを作成し、ステートメント FBlueprintCompiledStatementLHS に内部ピン「Switch」の変数を渡すことで、内部ピン「Switch」の値が False のときに実行ピン「B」に実行制御が移るという処理を実現します。
実行ピン「A」に実行制御を移す処理は、関数 GenerateSimpleThenGoto を呼び出して定義します。

5. メニューを追加

ノード「MyManualSwitch」を右クリックしたときに表示されるメニューに対し、項目を追加する方法を説明します。
今回追加するメニューは、内部ピン「Switch」の状態によって異なることに注意が必要です。

  • 「Execute A」:実行ピン「A」を実行するように設定する
  • 「Execute B」:実行ピン「B」を実行するように設定する

ノードを右クリックしたときに表示されるメニューへ項目を追加するためには、メンバ関数 GetNodeContextMenuActions をオーバーライドし、項目を追加する処理を定義します。

void UK2Node_MyManualSwitch::GetNodeContextMenuActions(class UToolMenu* Menu, class UGraphNodeContextMenuContext* Context) const
{
    Super::GetNodeContextMenuActions(Menu, Context);

    FToolMenuSection& Section = Menu->AddSection("K2NodeMyManualSwitch", NSLOCTEXT("K2Node", "MyManualSwitchHeader", "My Manual Switch"));

    // 内部ピン「Switch」の値を取得する
    UEdGraphPin* Pin = FindPin(SwitchPinName);
    bool bExecuteB = Pin->GetDefaultAsString() == "true";

    if (!bExecuteB)
    {
        // メニュー項目「Execute A」を追加する
        Section.AddMenuEntry(
            "ExecuteA",
            LOCTEXT("ExecuteA", "Execute A"),
            LOCTEXT("ExecuteATooltip", "Switch to execute A pin"),
            FSlateIcon(),
            FUIAction(FExecuteAction::CreateUObject(const_cast(this), &UK2Node_MyManualSwitch::ToggleSwitch)));
    }
    else
    {
        // メニュー項目「Execute B」を追加する
        Section.AddMenuEntry(
            "ExecuteB",
            LOCTEXT("ExecuteB", "Execute B"),
            LOCTEXT("ExecuteBTooltip", "Switch to execute B pin"),
            FSlateIcon(),
            FUIAction(FExecuteAction::CreateUObject(const_cast(this), &UK2Node_MyManualSwitch::ToggleSwitch)));
    }
}

最初に UToolMenu のメンバ関数 AddSection を呼び出してメニューのセクション(構造体 FToolMenuSection)を追加します。
続いて内部ピン「Switch」の値を取得し、構造体 FToolMenuSection のメンバ関数 AddMenuEntry を呼び出すことによってメニュー項目を追加します。
内部ピン「Switch」の値によって、追加するメニュー項目を変えている点に注意しましょう。

メンバ関数 AddSection の第5引数には、内部ピン「Switch」の値を "true" から "false" に、もしくは "false" から "true" に切り替える処理を定義したメンバ関数 ToggleSwitch を指定します。
メンバ関数 ToggleSwitch の定義を次に示します。

void UK2Node_MyManualSwitch::ToggleSwitch()
{
    Modify();

    // 内部ピン「Switch」の値を変更する
    UEdGraphPin* Pin = FindPin(SwitchPinName);
    if (Pin->GetDefaultAsString() == "true")
    {
        Pin->DefaultValue = "false";
    }
    else
    {
        Pin->DefaultValue = "true";
    }

    // ブループリントエディタにデータが変更されたことを通知
    FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(GetBlueprint());
}

内部ピン「Switch」の現在の値を確認し、現在の値と反対になるように値を設定しています。

最後に関数 FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified を呼び出し、ブループリントエディタに対してブループリントアセットが変更されたことを通知します。
この関数を呼び出さないと、ブループリントエディタ上からはブループリントアセットが更新されていない状態としてユーザに見えてしまいます。
具体的にはブループリントアセットが変更された状態であるのに、コンパイルボタンがコンパイル不要のままとなってしまうという現象が発生します。

作成したノードの利用

作成したノードを利用してみましょう。
Actorを作成してブループリントエディタを開き、右クリックでメニューを表示して検索ボックスに「mymaualswitch」を入力します。

ノード「MyManualSwitch」の検索

カテゴリ「Flow Control」に追加されているノード「MyManualSwitch」を選択して配置します。

Actorの作成

作成したActorをレベルに配置して実行すると、現在の内部ピン「Switch」の値によって実行されるピンが変化することが確認できます。
内部ピン「Switch」の値は、ノード「MyManualSwitch」を右クリックしたときに表示されるメニューから変更できます。

ノード「MyManualSwitch」のメニュー