執筆バージョン: Unreal Engine 5.3
|
こんにちは!今回はUMGにおける初期化処理(+α)に関して説明いたします。
「このタイミングで初期化を行いたいんだけど、どのイベントを呼べばいいか分からない……」
そんな時に参考にしていただけると嬉しいです!
そもそもUMGとは…??という方にはこちらの資料がおすすめです。
→猫でも分かるUMG(エピック ゲームズ ジャパン様ドクセルスライドのリンク)
UMGにおける初期化イベント
初期化イベントは複数あり、使用用途によって使い分ける必要があります。
● Construct
● PreContruct
● OnInitialized
PreConstruct / Construct はウィジェットを開いたときにデフォルトで置かれているノードですね。
Construct

呼ばれるタイミング
● Viewport上に追加されたとき(ランタイム中のみ)
<例> ○ 自身や、自身を含む親ウィジェットがAddToViewportされた
○ 既にViewport上に存在するウィジェットにAddChildされたとき
「描画された瞬間に呼ばれる」わけではないのでVisibilityを変更した際には呼ばれませんが、
RemoveFromParentノードなどで一度Viewport上から削除されたあと、
再びAddToViewportされたときに再度Constructが呼ばれます。(ActorのBeginPlayと非常に似た動きをします)
※エディター操作中にはConstructが呼ばれることはないため、その点は注意が必要です。
PreConstruct

呼ばれるタイミング
● Constructと同タイミング(ランタイム中)
● ウィジェットのデザイナーモードで何かしらの更新を行ったとき
<例> ○ デザイナモードの画面を開いたとき
○ コンパイルや子ウィジェットの配置、プロパティの変更などを行ったとき
UE 4.16 から追加されたこのイベントは、ランタイム中はConstructと同じ動きをしますが、
Constructと決定的に異なる点としてエディター操作時にも実行される、というものがあります。
『ゲームを起動しないとデザインの反映を確認できない……』そんなときに積極的に使用していきたいイベントです。
また、引数のIsDesignTimeはエディター操作中にTrueを返し、ランタイム中だとFalseを返すため、
エディター操作中に実行したくない処理(安全にアクセスできない値を触っている・単純に負荷が高い処理を走らせている etc…)は、
IsDesignTimeでブランチを切ってランタイム中のみ通るように対策することを強くおすすめします。

※PreConstructは【Editor Preferences】 -> 【Content Editors】 -> 【Widget Designer】 -> 【Execute Pre Construct Event】 をオフにすることで呼ばれなくなります。
PreConstructが原因でエディターがクラッシュしている可能性があるときは、一度こちらをチェックしてみてください。
注意点
UE4時点では上記のタイミングでのみ呼ばれていましたが、UE5からウィジェットアセットのサムネイル描画が実装されたため、
上記で説明した場面のほかに、ウィジェットにサムネイル画像が設定されていないときのみ、以下の場面でサムネイル再描画のためにPreConstructが呼ばれているようです。
● ウィジェットのグラフモード画面でコンパイルを行ったとき
● ウィジェットアセットをマウスオーバーしているとき
サムネイル画像は各ウィジェットのツールバーにある 【Class Settings】 -> 【ThumbnailImage】 で設定することができます。

UE4と決定的に異なる挙動をしているため、今後のバージョンで修正される可能性は高いと思われますが、
うっかりPreConstructにActorやObjectをスポーンさせる処理を書いてしまうとクラッシュの原因になるので、十分にお気を付けください。
(DevCommunity:コンテンツブラウザ上でWidgetアセットにカーソルを合わせるとPreConstructが呼ばれる)
OnInitialized

呼ばれるタイミング
● Widgetが作られたとき(一度きり)
Construct / PreConstructと異なり、ウィジェットが生成された瞬間に一度だけ呼ばれ、
AddToViewportやAddChildなどでは呼ばれず、再度ウィジェットが生成されるともう一度呼ばれます。
あまり使用されることの少ない印象ですが、デリゲートのバインド処理など複数回呼ぶ必要のない処理に関してはこちらを使用すると安全です。
ランタイム中に呼ばれるタイミングを一例としてまとめると以下の画像の通りになります。
● AddToViewport の場合

● AddChild の場合

ランタイム中のUMGのライフサイクルはこちらの資料に詳しく書かれていますので、
合わせてご覧いただくと理解がとても深まります。
→UE4で作成するUIと最適化手法(エピック ゲームズ ジャパン様ドクセルスライドのリンク)
例外
ListView
・ListViewで使用されるEntryWidgetに関しては『再生成』ではなく『使いまわし』のため、
前のウィジェットの変数情報などが残っている場合があります。
使い回された場合はConstruct / PreConstruct / OnInitialized に処理は通らないため、
EntryWidget特有のイベントであるEventOnListItemObjectSetイベントを使用して初期化を行うことをおすすめします。

(ListViewの解説記事→UE4.20で追加されたListViewウィジェットについて)
+α
終了処理
Blueprint上に公開されている関数の中で終了処理は一つだけです。
Destruct

先ほどの画像内でも表記しましたが、呼ばれるタイミングは以下の通りです。
● Viewport上から削除されたとき(ランタイム中のみ)
<例> ○ 自身や、自身を含む親ウィジェットがRemoveFromParentされた
○ 既にViewport上に存在するウィジェットにRemoveChildされたとき
Constructの対となる終了処理のため、Construct同様こちらもVisibilityを変更した際には呼ばれることはありません。
Visibilityが変わった時に何かしらの更新を行いたい!
初期化とは話がズレますが、「Widgetを表示する度に処理を走らせたい……でも非表示の度にRemoveFromParentを呼びたくはない……」と悩んでいる方、
OnVisibilityChangedデリゲートを使用してみてはいかがでしょうか?

こちらのデリゲートはVisibilityが変化したときに発火します。
引数のInVisibilityは変化後のVisibilityが渡されるため、表示された瞬間(Visible / Not Hit-Testable × 2種類)にのみ処理を通すことが可能です。
自身の子ウィジェットのOnVisibilityChangedデリゲートをバインドしたい場合は、
【Details】 -> 【Events】からもイベントとして同様に呼び出すことができます。

一点だけ注意することがあるとすれば、OnVisibilityChangedデリゲートはUUserWidgetで定義されているため、
OverlayやCanvasPanelなどのPanelWidgetやImageやListViewなど、標準で用意されているWidgetの多くはOnVisibilityChangedデリゲートが使用できません。
(CommonUIのCommonButtonBaseなど、使用できるものもあります)
自分が作成したウィジェットで使用できるデリゲートとしてご承知おきください。
Tips
ConstructとDestructは、ウィジェット内でそれぞれ『BeginPlay』『EndPlay』と検索してもヒットします。便利ですね!