【UE】UINavigationプラグインでUIの実装を効率化しよう

はじめに

こんにちは。株式会社アドグローブ ゲーム事業部エンジニアの牧です。

皆さんは「UINavigation」というプラグインをご存じでしょうか?

本記事では、UI実装の効率化に役立つ
「UINavigation」の使用方法に関して、一部実装も踏まえてご紹介させていただきます。

UINavigationとは

マウス、キーボード、ゲームパッド、またはそれらの組み合わせで操作できる UMG メニューを構築できるUnreal Engineのフレームワークです。
カーソル操作などのUIの基本的動作を担ってくれるので、実装時間を大幅に短縮することができます。

Fab(旧:Unreal Engine マーケットプレイス)にて無料でダウンロードすることができます。

導入手順

UEのプラグインをプロジェクトにダウンロードする手順は割愛させていただきます。
UE公式ドキュメントなどご参照ください。

プラグインを有効にしてから、
プロジェクトのBuild.csにUMGとUINavigation のモジュールを追加します。

PublicDependencyModuleNames.AddRange(new string[]
{
    "UMG",
    "UINavigation",
});

これでUINavigation の機能をBP、C++で使用できます。
では、実際に使ってみましょう。

UINavigation のセットアップ

こちらではセットアップに最低限必要な作業をピックアップしました。 詳しくはプラグイン作成者様のチュートリアルがありますのでご参照ください。
「UI Navigation 3.0 Tutorial - Basic Setup」 - YouTube

1. プロジェクト設定

プロジェクト設定>UserInterface>Focus を「Never」に設定します

2. UINavComponentの作成

UINavComponentクラスを継承してWidgetBlueprint(以下WBP)を作成します。
UINavComponentはUIメニュー内でのボタンにあたる部分のベースクラスです。
各要素間のフォーカスの移動を自動で制御してくれます。
Buttonの名前を「NavButton」に設定が必要です。

3. UINavWidgetの作成****

UINavWidgetクラスを継承してWBPを作成します。
UINavWidgetはUINavComponentで作成した各ボタンを配置したメニュー画面に当たるWidgetのベースクラスです。

4. UINavPCの作成

UINavControllerクラスを継承したPlayerControllerのBlueprintを作成します。
UINavigation の機能を使用するには、PlayerControllerにUINavPCComponentがアタッチされている必要があります。
UINavControllerを継承すると自動でアタッチしてくれます。

以下のようにUINavControllerクラスを継承してcppクラスを作成すると拡張がしやすいです。

#pragma once

#include "CoreMinimal.h"
#include "UINavController.h"
#include "UINavTestPlayerController.generated.h"

UCLASS()
class UINAVTEST_API AUINavTestPlayerController : public AUINavController
{
    GENERATED_BODY()

public:
    virtual void OnRootWidgetAdded_Implementation() override;
    virtual void OnRootWidgetRemoved_Implementation() override;
};

5. Widgetを生成して表示する

作成したUINavWidgetのWBPを生成します。
最初のUINavWidgetが生成される際にイベントが呼び出されるので、以下のようにコードを組むことでUIの入力に切り替えることができます。

// UINavTestPlayerController.cpp

// 最初のUINavWidgetが生成された
void AUINavTestPlayerController::OnRootWidgetAdded_Implementation()
{
    Super::OnRootWidgetAdded_Implementation();

    bShowMouseCursor = true;
    UWidgetBlueprintLibrary::SetInputMode_GameAndUIEx(this);
}
// すべてのUINavWidgetがViewportから外された
void AUINavTestPlayerController::OnRootWidgetRemoved_Implementation()
{
    Super::OnRootWidgetRemoved_Implementation();

    bShowMouseCursor = false;
    UWidgetBlueprintLibrary::SetInputMode_GameOnly(this);
}


何とこれだけで、メニューが動いてしまいます!簡単ですね
デフォルトの設定でもキーボード、マウス、コントローラーで上下移動、選択、キャンセルの操作が可能です。

機能の実装

セットアップが出来たところで、実際にメニューを作るために使用できる主な機能についてご紹介いたします。

1. イベントについて

UINavWidgetにはWidgetの操作に必要な各種イベントが用意されており、それらを用いることでプロジェクトの要件に沿ったUIを作成することが可能です。

OnGainedNavigation
 Widgetがナビゲーションを取得した時に呼び出されます
OnLostNavigation
 Widgetがナビゲーションが失った時に呼び出されます
OnNavigate
 ボタンのフォーカスが移動した時に呼び出されます
OnSelect
 ボタンが選択された時に呼び出されます
OnReturn
 ReturnToParent(Widgetを閉じる処理) が実行された時に呼び出されます
 ReturnToParentはキャンセルキーを押した時などにも呼ばれます
OnInputChanged
 入力タイプが変更されたときに呼び出されます
 コントローラ―とキーボードの入力の切り替わりを検知できます
PreSetup
 このウィジェットが UINav ロジック用にセットアップされる前に呼び出されます
 セットアップはWidget生成時や、メニューの切り替えを行った際に呼び出されます
OnSetupCompleted
 このウィジェットの UINav ロジック用のセットアップが完了したときに呼び出されます

2. Widgetの切り替え

上記イベントを用いてWidgetの切り替えを行ってみます。
以下のようにイベントの実装部を継承することで操作に合わせてメニューの切り替えなど、UIの処理を実装することができます。

手順
1. MainWidgetとSubWidget用のクラスとWBPを作成する
2. SubWidgetのWBPクラスを設定(今回はWBPから設定)
3. MainWidgetのボタンが選択されたらGoToWidget()でSubWidgetを呼び出す

#pragma once

#include "CoreMinimal.h"
#include "UINavWidget.h"
#include "MainWidgetBase.generated.h"

UCLASS()
class UINAVTEST_API UMainWidgetBase : public UUINavWidget
{
    GENERATED_BODY()

public:

    /**
    * ボタンが選択された
    * @param Component 選択されたボタン
    */
    virtual void OnSelect_Implementation(UUINavComponent* Component) override;

private:

    /** 次に表示するUIのクラス */
    UPROPERTY(EditAnywhere)
    TSubclassOf<UUINavWidget> NextUIClass;
}
// MainWidgetBase.cpp

void UMainWidgetBase::OnSelect_Implementation(UUINavComponent* Component)
{
    Super::OnSelect_Implementation(Component);

    // 次のUIを生成してナビゲーションを移動
    GoToWidget(NextUIClass);
}
#pragma once

#include "CoreMinimal.h"
#include "UINavWidget.h"
#include "SubWidgetBase.generated.h"

UCLASS()
class UINAVTEST_API USubWidgetBase : public UUINavWidget
{
    GENERATED_BODY()
};

二つのWidgetを切り替えることは出来ましたが、このままだと切り替わる際に前のWidgetが消えてしまいます。
Widgetの表示を残したい場合は、GoToWidget()の引数で設定できます。
Widgetの表示を残す際に、フォーカスをしてほしくない場合はWidgetのVisibilityをHitTestInVisbleに設定しましょう。
またRootWidget(最初に生成したUINavWidget)もキャンセル時に消えてしまいますが、 bAllowRemoveIfRoot をfalseにすることで消さずに残すことも可能です。

以下のように設定ができます。

// MainWidgetBase.cpp

void UMainWidgetBase::OnSelect_Implementation(UUINavComponent* Component)
{
    Super::OnSelect_Implementation(Component);

    // 次のUIを生成してナビゲーションを移動
    GoToWidget(NextUIClass, false);

    // falseにすることで、Rootの場合は削除されなくなる
    bAllowRemoveIfRoot = false;

    // フォーカスしないようにする
    SetVisibility(ESlateVisibility::HitTestInvisible);
}

表示を残したまま切り替えができました。

終わりに

今回はUINavigationの使用方法に関して紹介してみました。
便利なプラグインを用いて実装時間を短縮できれば、より細部のクオリティアップに力を注ぐ事ができるようになります。
アニメーションや選択カーソルの制御など紹介しきれなかった機能もまだまだありますので、 UIをこれから実装したいと考えている方は、ぜひ使用してみてはいかがでしょうか。
この記事が少しでも誰かのお役に立てれば幸いです。最後までお読みいただきありがとうございました。

参照サイト


アドグローブでは、さまざまなポジションで一緒に働く仲間を募集しています!
詳細については下記からご確認ください。みなさまからのご応募お待ちしております。

採用情報