見出し画像

Editor Utility Widget を使ってみる

2020/09/16 初稿

Editor Utility Widget とは

UE4をご使用の皆様、
「Editor Utility Widget」機能は活用されてますでしょうか?

初耳な方に端折りに端折って説明すると、プレイしていない状態で機能するウィジェットで、「エディタの拡張」を目的とするものです。
実行可能な処理に違いはあるものの、構成は通常ウィジェットとほぼ同じ。ブループリントだけでアセットを操作する等さまざまな機能を作成できるので、「少しサポートする仕組みが欲しい!」というときなど、プランナーだけでちょっとした拡張機能を作成できてしまいます。神機能。ありがたいですね! いずれプランナーやデザイナーであれば「使えて当然」な機能になるかもしれないな、と思っています(すでにそうかも?)

ちなみに、UE4.24.3 では、特になにも設定を必要とせずに使用できます。
「Editor Utilities」カテゴリの「Editor Utility Widget」で作成します(もうひとつの「Editor Utility Blueprint」はウィジェットが無いEditor Utility機能で、主に処理だけが必要なときに使います)。プラグイン「Editor Scripting Utilities」項目をONにしておくと、使える処理が増えるのでONにするのがオススメです(Scripting項目にあります)。

画像59


簡単な説明はここまで。以降では「Editor Utility Widget」を使った簡易な「リスト機能」の作成事例をご紹介したいと思います!

こういった「リスト機能」は開発が進むとしばしば起きる「アセットやアクタを探すのが面倒…」という問題に対して有効です。作りも難しくありませんので、「Editor Utility Widget」の入門として参考にしていただければと。

なお、ウィジェット部分は通常のゲームウィジェットと同様のつくりですので、簡易的な説明に留めさせていただきます。


基本の基本

と、その前に基本的な使い方を。さらっとご覧ください。
「新規作成」から「Editor Utility Widget」を作成します。
図のようにボタンとテキストブロックを追加。

画像58

画像5

ボタンの「On Click」イベントで、テキスト変更するイベントを作ります。
コンパイルして、コンテンツブラウザから「Editor Utility Widget」を右クリックします。「Run Editor Utility Widget」を選んでください。

画像6

下図のように、ユーティリティウィジェットが別ウィンドウで実行されることを確認できるはずです。ボタンを押すと、設定したとおりに変更されることがわかりますね!このように、基本はウィジェットと同じです。

画像57

ゲームでは使えない処理が多数あります。
たとえば「Get Selected Actors」。これは現在エディタ(レベル上)で選択しているアクタを取得する処理です。上記のグラフを改変してみましょう。

画像9

このように組めば、現在選択しているアクタをウィジェットに表示できます(非常に雑な組み方ですが)。うまく使えば、色々なアクタの情報を取得してリスト風にして編集…などができそうですね。

画像10


「Editor Utility Widget」でリストを作成する

本題です。リスト機能を作っていきます。

まずは、完成した状態です。
上部テキストボックスにデータテーブルのパスを入れると、その下にあるスクロールボックスに、テーブルに保存されている情報がリストで表示されるという仕組み。また右下の追加ボタンでファイルをリストに追加し、セーブボタンで保存します(サムネイル表示も…とも考えましたが、ざっと調べて簡単にできる方法が見つからなかったのでやめました。ご存知の方いましたら教えてください!)

画像11

ウィジェット構成は基本的な「親ウィジェット(スクロールボックスなど)」に「子ウィジェット(テキストのウィジェット)」を追加していくという方式です。

機能一覧

- コンテンツブラウザの選択しているファイルをリストに加える。
- レベルの選択しているアクタをリストに加える。
- リストはデータテーブルで保存したものを読み込む。
- データテーブルに書き込むこともできるようにする(セーブする)
- リストにはファイル名、クラス名を表示する(拡張できるようにもする)
- リストのアイテムは、「Open」ボタンで開く機能をつくる。
- リストの内容は「×」ボタンで各項目削除することができる。
- レベルのアクタのみ「Search」ボタンで選択状態にする。
- 各項目の動作をウィンドウ下部に履歴表示する。


親ウィジェット(リスト)「EUW_CLink」

「Run Editor Utility Widget」で実行する、ベースとなるウィジェットです。
なおファイル名および各パーツの名前はテキトーです。

画像13

要素

- データテーブルのパスを入力するテキストボックス(Editable Text)
- データリスト表示スクロールボックス
- 「↺」リロードボタン(パスのデータテーブル内容でリストを更新する)
- 「Add to the list」ボタン(コンテンツブラウザのアセットを追加する)
- 「Add the world actor to the list」ボタン(レベルのアクタを追加する)
- 「Saving」ボタン(リスト内容をデータテーブルに保存する)
- 「Open Link Data Table」ボタン(リストのデータテーブルを開く)
- 画面下部スクロールボックス:操作履歴を表示する

子ウィジェット(リストアイテム)「EUW_Item」

画像13

要素

- アクタの種類を示すラベル表示
- アクタ(ファイル)名の表示
- 編集可能なテキストボックス(コメントを記載するEditable Text)
- クラス名の表示
- 「Open」ボタン(アセット・レベルのアクタ詳細を開く)
- 「Search」ボタン(レベル上にあるアクタを選択する)
- 「×」ボタン(リストから当該項目を削除する)

履歴メッセージ用ウィジェット「EUW_SubText」

履歴表示としてのメッセージは、ベースウィジェット下部のスクロールボックスに追加します。

画像14

中身を作成していきます。

構造体「ST_UtilityData」の作成

必要なデータを保存する構造体を作っておきます。「EUW_Item」側で情報を保存します。データテーブルで書き換えをしますので、ほぼテキスト型にしています。

画像15


構造体「ST_UtilityDataSet」

上記構造体を含めた変数。こちらは保存せずリスト上だけで参照します。
「Integer」型、ウィジェット自体を保存する「EUW_Item」型、データテーブル行の名前を保存する「Name」型、CSV用のテキストを保存する「Text」型を加えた構造体になっています。

画像16

----

「EUW_Item」ウィジェット内の処理

まずはリストの子要素となるウィジェットを作成しておきます。
各ボタンのイベントは親側で処理しようと思いますので、イベントディスパッチャーで実装します。


「SetWidgetData」関数

画像17

ウィジェット表示などのデータを設定する関数です。
親でこのウィジェットを生成するときに呼び出します。

画像18

受け取った値を変数にして、各テキストに設定します(「IsWorldActor」はレベル上のアクタ選択ボタンを有効化するかを決定します)。

画像19

最後に「IsWorldActor」値と「Class」名でラベルカラーを設定します。


OnClicked(OpenButton)イベント

画像20

「Open」ボタンを押したときのイベント。
イベントディスパッチャーで、保存した「Path」値を渡します。


OnClicked(DeleteBtn)イベント

画像21

リスト項目を削除する「×」ボタンを押したときのイベント。
イベントディスパッチャーで保存した「RowName」値を渡します。

画像では「Path」値「RowName」値の2つを渡していますが、現状バインド先では「RowName」値だけを使っています。


OnTextCommitted(EditableText )(CommentBox)イベント

画像22

コメント用のテキストボックスが編集されたら呼び出されるイベント。
イベントディスパッチャーで保存した「Comment」値と「RowName」値を渡しています。

使用するのは「Comment」値と「RowName」値だけですが、
ひとまず「ST_Utility Data」型にして渡しています。


OnOnClicked(WoerldSelectButton)イベント

画像23

レベル上のアクタをサーチするボタンを押したときのイベント。
イベントディスパッチャーで保存した「Path」値を渡します。


EUW_CLink 内の処理

変数:「UtilityDataSets」配列の作成(構造体「ST_UtilityDataSet」型)

画像24

変数:DataPath(Text型)の作成

画像25

変数:Overwrite(Boolean型)の作成

画像26


「Initialization」イベント

初期化処理を作成します。

画像27

「Construct」イベント

Constructから呼び出します。
「Editor Utility Widget」では「Run」でウィジェットを表示したときに、「Construct」が実行されます。

画像28


Set Sub Message 関数

下図のような履歴メッセージとしてテキストを追加する処理です。

画像29

画像30

受け取ったテキストが空でなければウィジェット(EUW_SubText)を追加、
追加時に受け取ったテキストとテキストカラーを設定する仕組みです。
すでにリスト上に一定量のウィジェットがあれば最初のものから消去していくようにします。


SetDatatableData イベント

画像31

ここでは、つぎのような処理を作成しています。

- パスからデータテーブルの参照を作成
- データテーブルから保存データを読み込む
- 保存データをもとに子ウィジェットを追加する(「AddListItem」関数)

画像32

「Get Asset Registry」の「Get Asset by Object Path」ノードでアセット情報が返ってくるので、Castして変数化します。

画像33

この「Get Asset by Object Path」ノードなど「Get Asset~」 から始まるノードはアセット情報を取得するノードが多く、非常に使い勝手の良いのでしばしばお世話になります。
その後のマクロ「IsDataTableClass」では、受け取った「Asset Data」のクラスが「DataTable」と一致するか? を確認しています。

画像34


続けてデータテーブル変数から「Get Data Table Row Names」を「For each Loop」で回します。データテーブルから情報を抜き出す一般的な方法ですね。そして「ST_UtilityData」の各行をもとに、「AddListItem」関数でウィジェットとして追加します。

画像35


AddListItem関数

子ウィジェットを作成する関数です。

画像36

「EUW_Item」ウィジェットを作成して、
各イベントディスパッチャーとバインドします。

- 「Open」ボタン → Dispatcher_OnClickOpen 関数にバインド
- 「×」(Delete)ボタン → Dispatcher_OnClickDelete 関数にバインド
- EditableText  変更 → Dispatcher_OnClickTextCommit 関数にバインド
- 「Select」ボタン → Dispatcher_OnClickActorSelect 関数にバインド


Dispatcher_OnClickOpen関数

AddListItem関数の子ウィジェットにバインドしています。

画像37

受け取ったパスでアセットデータを取得。
存在すればアセットを開く「Open Asset」を使ってアセットを開きます。

画像38


DispaDispatcher_OnClickDelete関数

画像39

「UtilityDataSets」をFor each Loop で回して、受け取ったRowNameと一致するものを探します。一致するものをマーク(ローカル変数に保存)します。

画像40

マークされたものがあれば(ローカル変数 DeleteIndexLocal が-1でなければ)、上書き設定をONにして、その値を取り除く「Remove」処理。
また「Remove Child」で子ウィジェットも削除します。

画像41


Dispatcher_OnClickTextCommit 関数

画像42

「UtilityDataSets」をFor each Loop で回して、受け取ったRowNameと一致するものを探します。一致した変数のComment値を受け取ったComment値で置き換えます。CSV用のテキストもここで作成します(FormatCSVTextマクロでフォーマット)。
最後に変更していたら上書き変数をTrueにします。

FormatCSVTextマクロ

CSVに格納するためのデータを作成するテキストフォーマットマクロです。

画像43

CSVのフォーマットを確認するには、データテーブルを右クリックしてCSV形式でエクスポートしてエディタで確認します。

画像44


Dispatcher_OnClickActorSelect 関数

画像45

ConvertSearchPathマクロ

画像46

引数のパスから必要なテキストだけ抽出します。
そして、GetActorReference という処理で、引数で指定されたアクタを現在のエディタワールドから探し「SetSelectedLevelActor」で選択状態に変更します。

画像47


OnClicked(AddTotheListButton)イベント

「Add to the list」ボタンを押したときの処理です。
このボタンを押したとき、コンテンツブラウザで選択しているアセット(ファイル)をリストに追加します。

画像48

画像49

「Get Selected Asset Data」は現在コンテンツブラウザ上で選択してるAsset dataを取得します。「Is Valid Index」で存在するかチェックして、For each Loopで回してひとつずつ処理します。

画像50

「Is Not Duplication」関数ですでにリスト上に存在するかを確認します。

画像51

Asset data から必要なテキスト(String)値を取得し、「ST_UtilityData」型として変数にして「Add List Item」関数の引数にします。

RowNameは数値ではなくユニークな名称にしたほうがいずれ使いやすいかと考え「Create Random Row Name」マクロでランダムな文字を加えています。

「Add List Item」関数の戻り値でウィジェット情報を受け取ったものなどを含めて「ST_Utility DataSet」の配列にして親ウィジェット上で保存します。

追加したので上書き可能にする「Overwrite」変数をTrueにします。


OnClicked(AddTotheWorldActorButton)イベント

「Add the world actor to the list」ボタンを押したときの処理です。

画像52

画像53

基本的に内容は「Add to the list」と変わりません。

「Get Selected Level Actors」で選択しているアクタのActorObjectReferenceを取得して、For each Loopで回します。

「Get Path Name for Loaded Asset」でパスを取得して、Get Assetから情報を変数にします。

画像54


「Overwrite Event」イベント

情報を保存する処理を作成します。

リストに入れたリンク情報を取っておきたいのですが、「Editor Utility Widget」ではセーブ機能が使えず、UE4自体テキストファイルに書き込むような仕組みもありません(C++やPythonプラグインなどを使ってのファイル書き込み処理が一般的かもしれません)。しかしここでは、データテーブルに直接書き込む処理を使うようにしています。

レベル上にアクタを置き、その変数を編集&セーブすることでセーブ風にする簡易な方法もあります。しかし今回は「なるべくレベル上に余分なアクタを置かない」方針だったのでデータテーブルを保存する方法にしています。

画像55

まずCSVに書き込む処理「WriteCSVDate」関数を実行します。

WriteCSVDate関数

画像56

「直前のリスト配列数」と「現在のリスト配列数」が異なっているか、上書き可能を示す「Overwrite」変数がTrueであれば処理を行うようにします。

「WriteText」変数(テキスト型)を初期化して、「UtilityDataSets」の要素を For Each Loop で回します。抽出した CSVTxt 値を「WriteText」変数に足していきます。

最後に「Fill Data Table from CSV String」の箇所で、指定データテーブルに書き込みを実行します。

画像57

その後「Overwrite Event」イベントに戻り、データテーブルをSave Loaded Asset で保存します。

これは、Editor Utility の処理で対象のアセットを保存する命令です。

画像58

最後に情報を更新して初期化しています。

リストの作成は以上で完成です!
リロードなどの処理は「Initialization」の初期化と「Set Datatable Data」を行っているだけなので割愛します。

部分的に冗長だったり、不要な箇所があるのはご勘弁を…!
適宜調整いただければと思います。

これを基盤にして、ファイル検索機能を追加したり、情報を表示するカラムやプリセット機能など追加していけば、より使いやすくなるのではないでしょうか。開発にオススメの機能などあればお教えいただければ幸いです。

では!



この記事が気に入ったら、サポートをしてみませんか?
気軽にクリエイターの支援と、記事のオススメができます!
ゲームコンテンツ等を制作する会社で雑用を担当。UE4のヘビーユーザー。 マガジンでUE4講座を作ったり、拾ってきたTIPSなどをまとめて忘備録として使う予定です!😃 応援いただけると嬉しいです! 相談や要望もお待ちしています!