本記事はUnreal Engine 4 (UE4) Advent Calendar 2020 13日目の記事 です。
今回はC++メインということもありプログラマさん向けです。非エンジニアの方はプログラマさんに実装を相談又は手伝ってもらいましょう!
はじめに
「Dear ImGui」はご存知でしょうか?
C++を使って直感的かつ簡単にGUIを構築できる非常に優れたGUIフレームワークで、デバッグやツール用のUIを構築する際によく用いられます。従来のライブラリですとウィンドウを出したりボタンに処理を紐付けたりするのに手間がかかりましたが、Dear ImGui(以下、ImGui)なら非常にシンプルに書くことが出来ます。まずは下の参考記事をご覧ください!簡単過ぎて驚くと思います!
更に簡単だけでなく非常に多機能です。デフォルトで用意されている物を使うことで下図のようなGUIを作成することが可能です。
https://github.com/ocornut/imguiより
UE4の標準機能でデバッグ・ツール用UIを作る場合はUMGを使うことになりますが、上図のようなデバッグ向けのパーツを一から用意するのは正直大変です。また、ImGuiはUE4のビューポート上に別ウィンドウ的な形で表示されるので、下図のように自由にウィンドウを配置・整理することが可能です。デバッグ用UIが作業のジャマになっては本末転倒なのでこのような機能は非常に助かります(念の為触れますが、ゲームで実際に使うUIのようなアニメーションしたり様々なグラフィックを表示するのにはImGuiは向いていません。あくまでデバッグ・ツール用UI向けです)。
ええやん!(まだ使い方ぜんぜんわかってない) pic.twitter.com/xeNcOvVhWz
— おかず (@pafuhana1213) 2020年12月7日
そんな神ってるDear ImGuiはその知名度・注目度が年々増してきており、最近だとCEDEC AWARDS 2019 のエンジニアリング部門で優秀賞を受賞したり、FINAL FANTASY VII REMAKEにて採用されたりしています!すごい!
更に、有志による UE4対応版Dear ImGuiとして「Unreal ImGui」がなんと公開されています…!
github.com
更に更に、まだ触れていないのですが、実機上で表示したImGuiウィンドウをPCに繋いだマウス・キーボードで制御することが可能になる NetImGuiが組み込まれているようです!こりゃ触るしかないですね…!
github.com
https://github.com/sammyfreg/netImguiより
ということでやっと触ってみました。まだ調査しきれていない部分もあるのですが、ひとまず色々動いたので導入・基本部分について書いていこうと思います。
なお、ImGui自体の使い方についてはあまり解説しません。というより、先程の参考記事が素晴らし過ぎるのでそちらをご確認ください(あと、Dear ImGui公式のWikiも是非)
導入編
まずはUnreal ImGuiの各コード・ファイルをDLし、それらをプロジェクトのPlugins/Imguiフォルダに全て入れます。
そして、プラグインをビルドした後にImGuiプラグインを有効にします。
その後、PIEなどで実行中にコンソールコマンド ImGui.ToggleDemoを実行して以下のウィンドウが表示されたら導入成功です!
しかし、マウス操作をUE4のビューポートに取られてしまっているため、せっかく表示したImGuiのウィンドウを操作することができません…。
そんな時は慌てずに ImGui.ToggleInputを実行しましょう!これでImGui側の各ウィンドウを操作することが可能になります!
ImGuiに慣れるためにも、このデモを色々触ってみるのが良いと思います!特に、DemoWindowボタンを押すと出てくるサンプルはImGuiに用意された多くの機能を実際に見て触ることができるので非常に参考になります!以下のコードを眺めながら見るのもオススメです。
- Plugins\ImGui\Source\ImGui\Private\ImGuiDemo.cpp
- Plugins\ImGui\Source\ThirdParty\ImGuiLibrary\Private\imgui_demo.cpp
基本編
次は実際に自分のプロジェクト用のImGuiウィンドウを作ってみましょう!
まずは、実装するモデュールのBuild.cs に
PublicDependencyModuleNames.Add("ImGui");
または
PrivateDependencyModuleNames.Add("ImGui");
を追加します。
次に、Unreal ImGuiを使うための「おまじない」まとめをプロジェクトに追加します。
#pragma once#ifdef IMGUI_API#define WITH_IMGUI 1#else#define WITH_IMGUI 0#endif// IMGUI_API#if WITH_IMGUI#include <ImGuiModule.h>#include <ImGuiDelegates.h>#include <imgui.h>#endif// WITH_IMGUI
そして、新規Actor継承クラス AImGuiTest に ImGui用コードを追加します。
#pragma once#include "CoreMinimal.h"#include "GameFramework/Actor.h"// ImGui#include "ImGuiCommon.h"#include "ImGuiTest.generated.h" UCLASS() class UNREALIMGUI_API AImGuiTest : public AActor { GENERATED_BODY() public: // Sets default values for this actor's properties AImGuiTest(); protected: // Called when the game starts or when spawnedvirtualvoid BeginPlay() override; virtualvoid EndPlay(const EEndPlayReason::Type EndPlayReason) override; public: // Called every framevirtualvoid Tick(float DeltaTime) override; #if WITH_IMGUIvoid ImGuiTick(); #endif// WITH_IMGUI };
#include "ImGuiTest.h"// Sets default values AImGuiTest::AImGuiTest() { // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; } // Called when the game starts or when spawnedvoid AImGuiTest::BeginPlay() { Super::BeginPlay(); #if WITH_IMGUI// ImGuiのWorld Delegateに処理を登録 FImGuiDelegates::OnWorldDebug().AddUObject(this, &AImGuiTest::ImGuiTick); #endif// WITH_IMGUI } void AImGuiTest::EndPlay(const EEndPlayReason::Type EndPlayReason) { Super::EndPlay(EndPlayReason); #if WITH_IMGUI// ImGuiのWorld Delegateに登録した処理を解除 FImGuiDelegates::OnWorldDebug().RemoveAll(this); #endif// WITH_IMGUI } // Called every framevoid AImGuiTest::Tick(float DeltaTime) { Super::Tick(DeltaTime); #if WITH_IMGUI ImGui::Begin("ImGui Debug Order Test"); ImGui::Text("Actor Tick: Actor = '%ls', World = '%ls', CurrentWorld = '%ls'", *GetNameSafe(this), *GetNameSafe(GetWorld()), *GetNameSafe(GWorld)); ImGui::End(); #endif// WITH_IMGUI } #if WITH_IMGUIvoid AImGuiTest::ImGuiTick() { ImGui::Begin("ImGui Debug Order Test"); ImGui::Text("ImGui World Tick: Actor = '%ls', World = '%ls', CurrentWorld = '%ls'", *GetNameSafe(this), *GetNameSafe(GetWorld()), *GetNameSafe(GWorld)); ImGui::End(); } #endif// WITH_IMGUI
で、このActorをレベルに配置して実行すると以下のウィンドウが表示されるはずです!
こうして書いてみると少し長く感じるかもしれませんが、ImGuiのために追加したコードは30行ほどです!あとはTick関数・ImGuiTick関数にコードを追加していくことでいい感じのデバッグ・ツール用UIが作れそうです!
基本編の補足 < Tick と ImGuiTick の違いは何?>
Tickはお馴染みの処理なので説明不要ですが、ImGuiTickは ImGuiのWorld Delegateに登録することで動作させています。こうすることでポーズやSlomoなどでActorのTick頻度が変化した場合でもImGuiTick関数を毎フレーム走らせることができます。
下図が実際の動作の様子です。一時停止させるとActorのTickが止まるため、ImGuiウィンドウに表示されるテキストが減っていることが確認できます。
ということで、常に動いてて欲しい項目の場合は ImGuiのWorld Delegateに登録する形にするのが無難かと思います。なお、登録した処理は不要になったらちゃんと解除するようにしましょう!(AImGuiTest::EndPlay 参照)
なお、ImGuiにはMulti-Context Delegateというものもあるのですが…こちらはまだ十分に検証できていないので今回は解説しません。マルチプレイヤーゲームを作る際に活躍するもの(のはず)です。
設定編 <ショートカットキーToggle Input はいいぞ >
プロジェクト設定の Plugins/ImGui より、ImGuiのウィンドウ表示・操作に関する設定を変更することが可能です。色々項目がありますが、まずオススメなのがToggle Inputを設定することです。ここのToggle Inputにショートカットキーを設定することで、毎回 ImGui.ToggleInputを実行する手間を省くことが出来ます。
少し応用編 <テクスチャアセットをImGuiウィンドウで使う方法について >
と言っても、ほぼUnreal ImGuiの公式ページに書いてることなのですが…
h 側に以下を追加
UPROPERTY(EditAnywhere) UTexture2D* Texture;
BeginPlayに以下を追加
FImGuiModule::Get().RegisterTexture("TextureName", Texture);
EndPlayに以下を追加
FImGuiTextureHandle TextureHandle = FImGuiModule::Get().FindTextureHandle("TextureName");
FImGuiModule::Get().ReleaseTexture(TextureHandle);
Tick または ImGuiTick に以下を追加
ImGui::Image(TextureHandle, ImVec2(200,200));
という感じのコードを書くことで、以下のようにUE4側の画像データをImGuiで使うことが出来ます(最小限のコードなのでぬるぽ注意です。ちゃんとチェックコード入れましょう!)。
上のコードから少し察している方も多いかと思いますが、UE4とImGuiとの画像データのやり取りは文字列(FName)とIDデータを持つFImGuiTextureHandleを使って行います。そのため、ImGui側に渡す文字列は結構重要だったりします。ユニークである必要もあるため、恐らくアセット名を渡すケースが多いのではないかと思います。
FImGuiModule::Get().RegisterTexture(FName(*Texture->GetName()), Texture);
さいごに
雑多感じの内容にはなりましたが、日本語でのUE4へのImGui組み込みに関する情報が見当たらなかったのでこれから触る方には参考になる…はず…!と信じて書ききりました。
エディタ拡張をUMGで行う EditorUtilityWidget など便利な機能がUE4には沢山ありますが、「そこまで工数・作業時間は取れないけど、そこそこリッチ かつ 必要十分な機能を持ったデバッグUIが欲しい」という要望に応えるのは難しい場合があります。そんな時は是非ImGuiの利用を検討してみるのも良いかもしれません。
そして、是非使ってみた系の記事を…何卒…|д゚)チラッ
以上で本記事は終了です!
Unreal Engine 4 (UE4) Advent Calendar 2020 14日目の担当は、みんな大好きキンアジちゃん( https://twitter.com/kinnaji_blog ) です!神記事全裸待機してます!