【UE4】Editor Utility Widgetについてのあれこれ

皆さんお久しぶりです。

アンナプルナプランナーの吉田です。

昨今のUnrealのアップデートで、エディター拡張機能がどんどん追加されてきました。

今熱いエディター拡張機能の「Editor Utility Widget」についてのあれこれを大ボリュームで紹介したいと思います。

※この記事で使用しているUnreal EngineのVersionは4.23.0です。
※なるべく多くの人に理解してもらえるように、出来るだけ細かい説明をしますが、記事が膨大なため、多少専門的な説明は省いております。
あらかじめご了承ください。

目次

1,Editor Utility Widgetとは
2,Editor Utility Widgetを作ってみる
3,Editor Utility Widget テクニック集
4,汎用的な拡張ウィンドウを作ってみる

1,Editor Utility Widgetとは

まず、「Editor Utility Widget(以下EUW)」って? という方が多いかと思います。

EUWは、UE4.22から追加された機能で、
「User Widget」を使って、エディター拡張ができるものです。
↓公式ドキュメント
Editor Utility Widget
↓参考記事
[UE4]エディタ上で動作するツール・エディタ拡張をUMGで簡単に作れる Editor Utility Widget について
by Qiita(@EGJ-Kaz_Okadaより)

普通User Widgetは、ゲームを実行中に画面上にUIを出すために使われる機能ですが、
このEUWは、その機能をエディター上のツールとして使用することができます。

↓のような拡張ウィンドウを出せる

 

特徴

①非プレイ中でも機能する

→ゲームを実行していなくても、エディターツールなので機能を使うことができます。

②Editor UtilityなBlueprintが使える

→エディター上でのみ使えるBlueprint(アセットをセーブする「Save Asset」や、DataTableをReimportする「Fill Data Table from csv String」など)を使うことができます。

③特殊なWidget「Detail View」「Property View」が使える

↓参考
【UE4】メモ:実はEditor Utility WidgetはBlutilityの完全上位互換だった話【★★】
byキンアジのブログより

→Detail Windowなんかでよく使われているアセットの参照などのUIを使うことができます。

※Widgetを配置するだけでは使うことはできず、「Set Object」という関数で対象のObjectを指定することで初めて機能します。
(Single Property ViewはPropertyを指定しないとダメ)

EUWには上記の特徴があり、Unrealのエディター上で作業するうえで、とても有用なサポートツールを生み出す可能性を秘めています。

では、EUWの作り方~実行の仕方までを軽くご紹介しましょう。

2,Editor Utility Widgetを作ってみる

では簡単に、EUWを使ったテストを作ってみます。

UE4.23のバージョンのプロジェクトを開けましたら、「Content Browser」を開きましょう。
→どこにあるかわからない場合は、エディターの上のほうにある「Toolbar」内の「Content」というボタンを押すことで、表示できます。

「Content Browser」を開けたら、その左上にある「Add New」と書かれた緑色のボタンを押し、
出てきた小ウィンドウの中から「Editor Utilities」にカーソルを合わせ、更に出たウィンドウから
「Editor Utility Widget」を選択します。

これで、Content Browser上にEditor Utility Widgetアセットが出現したと思います。

出来ましたら、ダブルクリックして中を開いてみましょう。

開くと、通常のWidgetの編集時と同じウィンドウになります。

※Widgetについては、以下を参照してください。
UMG UI デザイナ

※Widgetのベストプラクティスについては以下を参照してください。
UMG Best Practices

では、試しにボタンを追加して、そのボタンが押されたら「Hello World」とViewportに出力される処理を作ってみましょう。

Widget エディター左上の方にある、「Palette」ウィンドウ内から「Button」というタブを見つけそれをWidget エディター中央の
「Designer」ウィンドウ内にドラック&ドロップしましょう。

※場所はどこでもいいですが、なるべく左上が良いです。

すると、ボタンらしきものがDesignerウィンドウに表示されたかと思います。

出来ましたら、今度はWidget エディター右上の「Graph」という部分をクリックしてください。
すると、Blueprint エディターに切り替わります。

切り替わりましたら、左側にあります「My Blueprint」ウィンドウ内の「Variable」という項目内の
「Button~~~」という、先ほどDesignerウィンドウに追加したものと同じ名前のものをクリックした状態で、
左下にある「Detail」ウィンドウ内の「Events」項目内の「OnClicked」というプロパティの右側にある、
緑色の+ボタンをクリックしてください。

すると、「On Clicked」という、このボタンが押されたときに発行されるイベントノードが自動的に置かれます。

ここまで出来ましたら、次は「On Clicked」ノードの▷ピン部分から適当なGraphウィンドウ内にドラッグ&ドロップをして、
出てきたウィンドウ内から「Print String」というタブを探してクリックします。

あとは、出てきたPrint Stringノードの「In String」プロパティに「Hello World」と打ちましょう。

出来ましたら、ウィンドウ左上の「Compile」ボタンを押します。

ここまで終わりましたら、一旦ウィンドウを閉じ、Content Browserに戻ります。

Content Browser内の作成したEditor Utility Widgetを右クリックして、「Run Editor Utility Widget」をクリックしてください。

すると、今作成したEditor Utility Widgetをもとにした拡張ウィンドウが画面内に現れるかと思います。

後は、出てきた拡張ウィンドウ内のボタンをクリックすると、「Hello World」という文字がView port上に現れるかと思います。

※4.23から日本語フォントが非対応になったため、日本語のフォントを使用したい場合は以下サイトをご覧ください。
UE4を4.23にバージョンアップしたら文字化けしたので日本語フォントを設定してみました

これであなたはEditor Utility Widgetの基本的な作成~実行をマスターしました。

次は、誰でも手軽に機能を追加・編集ができる汎用的なエディター拡張システムを作ってみたいと思います。
この機能は実際のいくつかの現場でも使っています。

3,Editor Utility Widget テクニック集

次にEditor Utility Widgetにまつわるワンポイントテクニックを紹介していきます。

①「Window」タブからEUWを起動

一度「Run Editor Utility Widget」で起動したことのあるEUWは、エディター左上にある、「Window」内の「Editor Utility Widgets」というタブから起動することも出来ます。

いちいちアセットを探して起動する必要がないので、覚えておくと便利です。

ただし、デフォルトのEUWの設定だと、エディターを一度落としてしまうと、Window内から消えてしまいます。

エディターを起動しなおした際もWindow内に表示したい場合は、EUWアセットの「Graph」モード時に「Class Default」を選択し、「Details」ウィンドウ内の「Settings」カテゴリの「Always Register with Windows Menu」という部分にチェックを入れると、起動した情報をエディターを落としても保持することができます。

また、この情報は各Projectの/Saved/Config/Windows/内にある
EditorPerProjectUserSettings.iniという設定ファイル内の
[/Script/Blutility.EditorUtilitySubsystem]
という項目部分にある「LoadedUIs=」というプロパティに保存されています。

・記述例

[/Script/Blutility.EditorUtilitySubsystem]
LoadedUIs=/APTextScriptPlugin/ScriptEditor/EUW/EUW/BP_EUW_Base.BP_EUW_Base
LoadedUIs=/APTextScriptPlugin/ScriptEditor/EUW/EUW/BP_EUW_Test.BP_EUW_Test

なので、ここを書き換えることで、EUWを起動せずともWindowの項目に追加することができます。

※あまり数が多いとエディター起動が遅くなったりするので、ご利用は計画的に。

②BlueprintからEUWを実行~Spawn and Registar Tab~

「Editor Utility Subsystem」というモジュールの中に、「Spawn and Registar Tab」という関数があります。

これは、Editor Utility Widgetアセットを指定することで、Editor Utility Widgetを起動させることができます。

他のEditor Utilityと合わせて使うととても強力なツールを作れるよ!

③Editor Utility Widgetの親子関係

EUWを作成する際、親クラスは「Editor Utility Widget」しか指定できません。

が、作成した後、「Graph」モードで「Class Settings」を選択した状態で「Details」ウィンドウ内の「Parent Class」から、自作したEUWを親にすることができます。

基本機能は親で実装し、子のプロパティなどで実装に変化を持たせると、より効率的な開発を行うことができます。

④通常のWidgetもEditor Utility Widget内に配置可能

Editor Utility WidgetもUser Widgetを継承しているので、通常のBPで作成したUser WidgetなどをDesignerウィンドウ内に配置することもできます。

インゲームで使うWidgetをプレイしないで簡易的に確認することもできますので、便利だと思います。

4,汎用的な拡張ウィンドウを作ってみる

「汎用的な」という言葉で今回目指す目標としては、
「機能の追加や削除、編集が用意に自在にできる」という要件を満たせるものとします。

内容的には、
「拡張ウィンドウ上に行単位でWidgetを表示し、その行1つ1つが1つの機能を保有している」
というものにしたいと思います。

簡単に作成した最終系をご覧ください。

このWidgetの行一つ一つを、編集しやすい作りにして、自由に簡単に拡張できるものを作っていきたいと思います。

・この仕組みの特徴

これから作成するエディター拡張システムの特徴としては、

1.Editor Utility Widgetを新しく作る際に、毎回Widgetなどを配置する必要がない
→手間が少ない、アセットのサイズを削減できる(Widgetは容量的に大きい)
2.機能を使いまわしたりが可能
→似たような昨日を実装する際に、同じBlueprintやWidgetを作る必要はなく、用途に応じて自由に追加ができる。

といったようなメリットがあります。

それでは早速機能を実装していきましょう。

Step.1 基本となるBlueprintなどのアセットを作成

※これからBlueprint内の説明を大量に致しますが、数が多いため画像のみに割愛するものが多くございます。
何かこちらについてご質問がある場合は s-yoshida@anapurna.co.jp までご連絡ください。

まず先に、ボタンやテキストなどの各種Widgetを一つ一つBlueprintクラスとして定義しておきます。

Content Browser左上の「Add New」から、「User Interface」→「Widget Blueprint」を選択してください。

名前は「BP_UIBox」とします。

出来ましたら、もう一つWidget Blueprintを作成してください。

今度の名前は「BP_WidgetParts_Base」とします。

一旦作成できましたら、今度「BP_WidgetParts_Base」を親とした子供のBlueprintを作成します。

Content Browser左上の「Add New」から、「Blueprints」→「Blueprint Class」を選択します。

そうして出てきたウィンドウ内の一番下の「All Classes」という場所をクリックし、現れた部分から
今作成した「BP_WidgetParts_Base」を選択して「Select」を押します。

これを8回ほど行い、できたアセットにそれぞれ名前を付けます。

・BP_Button_Base
・BP_CheckBox_Base
・BP_ComboBox_Base
・BP_Detail_Base
・BP_EditableText_Base
・BP_Slider_Base
・BP_SpinBox_Base
・BP_Text_Base

次に、各行の実行する関数用のクラスを作成します。

先ほどと同じようにContent Browser左上の「Add New」から、「Blueprints」→「Blueprint Class」を選択し、
一番下の「All Classes」という場所をクリックし、現れた部分から「Object」を選択して「Select」を押します。

名前は「BP_EUWFunction_Base」としておきましょう。

もう一つ、「Details View」Widget用にObject型のクラスを作成します。

先ほどと同じようにContent Browser左上の「Add New」から、「Blueprints」→「Blueprint Class」を選択し、
一番下の「All Classes」という場所をクリックし、現れた部分から「Object」を選択して「Select」を押します。

名前は「BP_DetailObject_Base」としておきましょう。

次に、この仕組みで使用するEnum・Structure・Interfaceを作成しておきます。

〇Enum

Content Browser左上の「Add New」から、「Blueprints」→「Enumeration」を選択します。
名前は「ENUM_ValueType」としておきます。

中身は以下のように3つの項目を追加し、「Float」「String」「Bool」という名前を付けておきます。

〇Structure

Content Browser左上の「Add New」から、「Blueprints」→「Structure」を選択します。
名前は「STRUCT_WidgetPartsInfo」としておきます。

中身は以下の型の項目を追加します。

・WidgetPartsClass            :BP_Widget_Parts_Base(Class)
・PartsName                   :String
・SlateChildSize              :Slate Child Size(Struct)
・DefaultStringValue          :String
・DefaultFloatValue           :Float
・DefaultBoolValue            :Boolean
・DefaultComboBox_Options     :String(Array)
・DefaultMinFloat             :Float
・DefaultMaxFloat             :Float
・DefaultDetailObjClass       :BP_DetailObject_Base(Class)

〇Blueprint Interface

Content Browser左上の「Add New」から、「Blueprints」→「Blueprint Interface」を選択します。
名前は「BP_IF_EUW_Function」としておきます。

中に以下二つのFunctionを作成します。

・「Init」
引数:BP_Widget_Parts_Base(Array),User Widget(Array)
返り値:なし

・「Destruct」
引数:なし
返り値:なし

最後に、EUWのクラスを作成します。

「Add New」→「Editor Utilities」→「Editor Utility Widget」を選択します。

名前は「BP_EUW_Base」とします

ここまで出来ましたら、次は作成したBlueprintの中に処理を書いていきます。

Step.2 Blueprintの処理を書いていく

まずは「BP_WidgetParts_Base」の中に処理を書きます。

このWidgetには、一切Widgetを追加しません。
のちに作成した子のWidgetのほうにWidgetを追加します。

BP_WidgetParts_Baseを開いたら、Graphモードにしましょう。
その中の「My Blueprint」内にいくつかの項目を追加していきます。

〇新規追加Function
「Get Input Value」(Pure)

「Call Input Event Delegate」

「Init」

〇新規Variable
→Type        :ENUM_ValueType
→Index       :Integer
→WidgetInfo  :STRUCT_WidgetInfo
→PartsName   :String

〇新規EventDispatcher
→InputEvent
引数:Integer
返り値:なし

ここまで出来ましたら、先ほど作成したBP_WidgetParts_Baseの子供のClass内のBPを書いていきます。

・BP_Button_Base

Designerの中身

※TextBlock96には、Detailウィンドウで「is Variable」にチェックを入れています。

TextBlockは4.23の日本語フォント非対応化の影響を受け、日本語を入力すると文字化けしてしまいます。
日本語を使いたい場合はフリーフォントサイトなどでダウンロードし、Unrealにインポートしてください。

EventGraphの中身

・BP_CheckBox_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Get Input Value」(Override)

・BP_ComboBox_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Set Options」

「Get Input Value」(Override)

・BP_Detail_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Init Detail Obj」

・BP_EditableText_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Get Input Value」(Override)

・BP_Slider_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Get Input Value」(Override)

・BP_SpinBox_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Get Input Value」(Override)

・BP_Text_Base

Designerの中身

EventGraphの中身

〇新規追加関数

「Get Input Value」(Override)

続いて、「BP_EUWFunction_Base」の中に処理を書いていきます。

最初にこのクラスに先ほど作成した「BP_IF_EUW_Function」インターフェースを設定します。

Blueprintを開いたら、「Class Settings」を押し、Detailsウィンドウ内の「interfaces」→「Implemented Interfaces」の「Add」ボタンを押します。
そして、出てきたウィンドウ内で「BP_IF_EUW_Function」を選択します。

その後一度Compileを押します。

そして、以下のようにBPを組みます。

〇新規追加関数

「BindWidgetInput」

「Execute」

※ほかにもいくつか関数を追加しましたが、長くなってしまうので後述で使用する関数のみ紹介します。

「GetInputValueByPartsName」

「GetWidgetPartsByPartsName」

「GetWidgetPartsByPartsName_Core」

〇新規追加Variable

→EUWInfo             :STRUCT_WidgetPartsInfo(Array)
→PartsWidgets        :BP_WidgetParts_Base Reference(Array)
→ExtraWidgets        :User Widget Reference(Array)
→ExtraWidgetsClass   :User Widget Class(Array)
→BaseExecuteIndex    :integer
→ParentEUW           :BP_EUW_Base

これでFunction用ベースクラスの定義は終わりです。

次に「BP_UIBox」を編集していきます。

まず「Designer」モードですが、以下のことを行います。

1,「Vertical Box」というWidgetを追加(Is Variable,Size to Contentにチェック)
2,Vertical Boxの子に「Horizontal Box」を追加
3,image Widgetを追加 ※別段必要ではないですが、見やすさのため。
→「Anchors」は一番右下の全画面表示モードに
→「Offset Left」,「Offset Top」,「Offset Right」,「Offset Bottom」の値はすべて0.0に設定
→自作マテリアルを設定(↓にノードを書いています)
→「Draw As」を「Box」に設定。
→その後「Margin」を0.5に設定。
→Color and Opacityは適当に。

※↑で使用したMaterialは以下のように組みます。(Content BrowserからAdd New → Materialを選択してアセットを作る)

※Material Domainを「User Interface」にしてください。
※Customノードの記述は以下です。

float2 v2 = abs((UV - 0.5) * 2);
float2 v2In = saturate(abs((UV - 0.5) * 2) + StepValue);
float OutCurve = step(distance(smoothstep(1 - StepValue,1,v2),float2(0.0,0.0)),1.0);
float InCurve = step(distance(smoothstep(1 - StepValue,1,v2In),float2(0,0)),1.0 - StepValue);
return float2(OutCurve-InCurve,InCurve);

次に「Graph」モードに切り替え、編集を行います。

〇新規追加関数

「AddWidgetParts」

「ConstructFunction」

「AddScriptWidget」

「AddExtraWidget」

〇新規追加Variable

→EUWFunction         :BP_EUWFunction_Base Reference
→FunctionClass       :BP_EUWFunction_Base Class
→WidgetParts         :BP_WidgetParts_Base Reference(Array)
→ExtraWidgets        :User Widget Reference(Array)

最後の下準備に、「BP_EUW_Base」の中を編集していきます。

まず「Designer」モードですが、以下のことを行います。
1,「Scroll Box」というWidgetを配置しましょう。
2,Scroll Boxの子に「Vertical Box」というWidgetを配置しましょう。
3,Vertical Boxは「Is Variable」にチェックを入れましょう。

次にBlueprint Graphのほうに処理を書いていきます。

〇新規追加関数

「AddUIBox」

〇新規追加Variable

→UIBox_Array         :BP_UIBox Reference(Array)
→FunctionClassArray  :BP_EUWFunction_Base Class(Array)
→WidgetParts         :BP_WidgetParts_Base Reference(Array)
→ExtraWidgets        :User Widget Reference(Array)

これで一通りの基本機能を実装することができました。

Step.3 エディター拡張機能を実装する

さて、長かったですが基本的な仕組みは仕上がったので、この機能を使って簡単なエディター拡張を作成してみます。

今回は簡単なもので、
レベル内にあるBlueprintClassのアクターのリストを作成する
というものを作ってみます。

最初に「BP_EUWFunction_Base」を継承したBPClassを2つ作成します。

Content Browserの「Add New」から「Blueprints」を選択し、出てきたParent Class選択画面で「BP_EUWFunction_Base」を選択してください。

作成したアセットは、「BP_LevelBPActorsList」と「BP_LevelBPActorsList_ClassInfo」としましょう。

最初に「BP_LevelBPActorsList_ClassInfo」の方を開き、Blueprint Graphで以下のように処理を書きます。

各種の「Parent ~~~」は、各種のイベントノードを右クリック「Add call to parent function」を選択することで表示できます。

〇新規追加関数

「Execute」(Override)

〇新規追加Variable

→BPClassAsset        :Object Reference

また、Class Defaultは以下の用に編集します。

・EUWInfo(Array)

Index0
WidgetPartsClass            :BP_Text_Base
PartsName                   :ClassName
SlateChildSize              :3.0,Fill
DefaultStringValue          :ClassName
DefaultFloatValue           :0.0
DefaultBoolValue            :False
DefaultComboBox_Options     :None
DefaultMinFloat             :0.0
DefaultMaxFloat             :0.0
DefaultDetailObjClass       :None
Index1
WidgetPartsClass            :BP_button_Base
PartsName                   :OpenAsset
SlateChildSize              :1.0,Fill
DefaultStringValue          :OpenAsset
DefaultFloatValue           :0.0
DefaultBoolValue            :False
DefaultComboBox_Options     :None
DefaultMinFloat             :0.0
DefaultMaxFloat             :0.0
DefaultDetailObjClass       :None

・BaseExecuteIndex        :1

出来ましたら一旦閉じ、次に「BP_LevelBPActorsList」の方を開き、Blueprint Graphで以下のように処理を書きます。

〇新規追加関数

「MakeBPAssetList」

また、Class Defaultの以下の値を変更します。

・EUWInfo(Array)

Index0
WidgetPartsClass            :BP_Text_Base
PartsName                   :AssetPath
SlateChildSize              :3.0,Fill
DefaultStringValue          :AssetPath
DefaultFloatValue           :0.0
DefaultBoolValue            :False
DefaultComboBox_Options     :None
DefaultMinFloat             :0.0
DefaultMaxFloat             :0.0
DefaultDetailObjClass       :None
Index1
WidgetPartsClass            :BP_Text_Base
PartsName                   :OpenAsset
SlateChildSize              :1.0,Fill
DefaultStringValue          :OpenAsset
DefaultFloatValue           :0.0
DefaultBoolValue            :False
DefaultComboBox_Options     :None
DefaultMinFloat             :0.0
DefaultMaxFloat             :0.0
DefaultDetailObjClass       :None

作成したら、一旦閉じて、先ほど作成した「BP_EUW_Base」アセットを再び開きましょう。

Class Defaultの「Function Class Array」という配列に、今作成した「BP_LevelBPActorsList」クラスを設定しましょう。

さて、これで機能はできました。

試しにActorClassのBlueprintをいくつか作成し、レベル上に配置します。

(Content Browserの「Add New」から「Blueprints」を選択し、出てきたParent Class選択画面で「Actor」を選択してください)

とりあえず3つ作成しました。

これを適当なレベル上にドラッグ&ドロップで配置します。

そうしたら「BP_EUW_Base」アセットを右クリック→「Run Editor Utility Widget」で実行してみましょう。

いかがでしょうか。

今まで説明した機能はすべてEditor内で完結する、つまりBlueprintのみで作ることができます。

また、私は今作成した機能を使って、弊社開発中ゲームのスクリプトのエディターを試作的に作成したりもしています。

皆さんのプロジェクトにも、是非Editor Utility Widgetを運用して開発を効率化し、よりクリエイティブな時間を多くとれるような運用になっていくことを心から望んでおります。

以上となります。