執筆バージョン: Unreal Engine 4.27
|
今回はUMGで作ったUIのレイアウトを元に3Dモデルの表示をしてみようと思います。
UMGを元に3Dモデルの表示を合わせる利点として
- スマホやモニターの縦横比が異なる画面でのズレを回避できる
- UMGを元に作るとアニメーションをUMGのアニメーションでつけることもできる
があげられます。
UnrealEditorのイテレーションのしやすいので、エディターでUIと3D空間のいち合わせを力技で合わせてもいいのですが、
やはり時間がかかります。
今回は2Dから3Dに変換する機能がUE4では提供されているのでそれらを使って作ってみようと思います。
今回の作るもの
- UMGで作成したImageWidgetの位置にカメラから任意の距離にモデルを配置する
- ImageWidgetのサイズに合わせてモデルのサイズを変更する

主な処理の流れ
- Widgetの座標空間上の位置・サイズを決める
- Widgetの座標空間上の位置をもとにカメラからのワールド空間での向きを求める
- 特定のカメラ距離に配置した場合の3D空間での位置・サイズを決める
- カメラの向きを基準にワールド空間でのRotationを求め 1・2・3で求めた位置・回転・スケールからTransformを作成する
- 4のTransformを対象のモデルを表示するActorをSetActorTransformで表示を変更する
表示領域を表すWidget(今回はImage)からViewport上の座標を求めます。

変換には「Local to Viewport」を利用します。

これを利用する場合対象のWidgetのGeometryが必要です。
Geometryの取得には「GetTickSpaceGeometry」を利用します。

「Local to Viewport」はLocalCoordinateの2D座標を以下のように座標を変換します。
左上が(0,0) 右下がGeometryの「GetLocalSize」の値になります。

更に今回はモデルの足元が原点のため下中央が原点に来るようにしたいと思います。

できれば、UMG上で設定をしたいので、「Slot as Canvas Slot」内の「Alignment」を元に原点を決めたいと思います。

この情報は「Slot as Canvas Slot」と「GetAlignment」を利用することにより情報を取得することができます。

サイズも同様に「Local to Viewport」を利用しViewport上の座標からサイズを求めます。

よく対象のプレイヤーの頭の上に名前を表示したりするときに3D座標から2D座標への変換を行いますが、今回はこの逆を行います。
「Deproject Screen to World」を利用することによりプレイヤーのカメラから見たViewport上の座標が3D座標ではどこを指すか変換することができます。

今回は任意のカメラからの距離に配置したいので、カメラからの向きの「World Direction」を利用し元にワールド空間内での座標を求めます。
「World Direction」はモデルをカメラに向かせるために計算にも利用します。

「Deproject Screen to World」を利用するPlayerContorllerはWidgetを表示しているPlayerControllerを利用するので「GetOwningPlayer」を利用して取得します。

3. 特定のカメラ距離に配置した場合の3D空間でのサイズを決める
表示領域を表すWidgetの左右の2点から3D空間上の横幅を求め、元の想定するサイズから表示するスケールを求めます。
※今回は簡単な方法で3D空間上でのおよそのサイズを求めています。
そのためカメラの画角により画面端で歪みが発生します…



アニメーションや画面サイズの変更が想定される場合はTickで毎フレーム設定してください。



Charactorのカメラ操作の後にSetActorTransformの設定する必要があります。
この場合WidgetBlueprintではTickGroupの設定できないので別Actorを用意し、
ClassDefaultからTickGroupを「PostUpdateWork」にTickのタイミングを変更し、SetActorTransformするようにして下さい。

まとめ
Widgetの座標変換と2D→3D変換の機能を理解するまで、難しいかもしれませんが、
UE4が提供するノードで細かい計算をせずにこのような表現ができるので試してみてはどうでしょうか?