みつまめ杏仁

アンリアルエンジン(UE4)でGUIを作るためにゴニョゴニョしてます。UIデザイナーの皆様の助けになれば幸いです。

3Dオブジェクトの上にWidgetをマーカーのように表示できたけどどうなってるの?なんか計算がややこしいのだけれど。

という長いタイトルを付けるのが最近のトレンドぢゃ、とじっちゃが言ってたのでさっそく実践してみたけど、なんというか全然キャッチーにならないのはセンスがないから?などと架空の祖父まで召喚してくだらない言い訳をつらつらと書きながら花粉の猛襲に苛まれる目をひたすら擦りながらキータイプもままならない状態でややこしい記事を書こうとしている折、皆様ますますご健勝のことと存じます。

さてさて前回はとりあえず動くところまでのチュートリアル的な記事を書きました。

少ない脳みそを駆使して解説をしてみようと思います。実際はこうやって書きながら自分の頭を整理してログを残すのが目的だったりします。

 

 さてさて

今回の処理で扱う重要な数値は以下の5つ

  • 描画のための画面サイズ  → Get Viewport Size
  • 追随するWidgetのサイズ  → Get Desired Size
  • 表示スケール  → Get Viewport Scale
  • アクターの表示位置  → ConvertWorldLocationToScreenLocation
  • 最終表示位置Set Position

 

 これらの値があれば計算できるのですが、ひとつ厄介な問題があります。それは最終的な表示サイズが必ずしも画面の大きさと同じとは限らないという問題です。

 たとえば開発中のエディタ解像度を 1920x1080 で進めたとしても、実際遊ばれる環境は、1080pじゃない場合が普通にありえます。フルスクリーンにしろウィンドウにしろユーザーに任されていて遊ぶ環境によって変わるため、UE4では表示する際に、表示されている解像度に応じて一定量のスケール値を掛けることで、元のレイアウトが崩れないように描画してくれます。

 また表示する際に指定する座標は、描画のための画面サイズであるエディタ解像度に準拠するので、スケールを掛けないように指定する必要があります。

 スケールが掛かっていたり掛かっていなかったりする値をうまく整理しながら計算しないといけないのです。

 

開発環境にてエディタ解像度を 1920x1080 にして、画面には 1280x720で表示している場合、上にあげた5つのノードで扱う値は、以下のようになっています。

 

f:id:hiyokosabrey:20190310012023p:plain

Get Viewpor Size は表示されている解像度です。(基本的に可変します)

Get Desired Size にはキャンバスに置いたときの値で、スケールが掛かっていません。

 

f:id:hiyokosabrey:20190310010520p:plain

 

f:id:hiyokosabrey:20190310012429p:plain

 

ここまではGET。最後にSET。

 

f:id:hiyokosabrey:20190310013029p:plain

 

このようになっているので、@ghosticgames さんの手順をみてみると、基本的に スケールの掛かった ViewportSize ベースで計算して、表示位置が確定した後に ViewportScale で割り算して、1920x1080空間用に合うようにしています。

f:id:hiyokosabrey:20190310014617p:plain

 

Get Desired Sizeで得た追随用パーツのサイズに ViewportScaleを掛けているのは、スケールを揃えないとこの後の計算がおかしくなるからです。

f:id:hiyokosabrey:20190310014943p:plain

 

ではこの辺で Clampさんのお仕事紹介と行きましょう。

Clampは3つの入力ピンがあります。

f:id:hiyokosabrey:20190310125803p:plain

Value から入った値が、Min と Max の範囲を越えても、Min(下限) と Max(上限) の範囲から出ないことを保証してくれるという代物。

下図はイメージ。

 

f:id:hiyokosabrey:20190310131747p:plain

例えば、Max に 1.0 が設定されている時に、 Value が 2.5 だとすると、強制的に 1.0 になります。

 

 この仕組みを使って、表示座標に制限を付けるのです。

 

 

f:id:hiyokosabrey:20190310133059p:plain

 アクターの表示位置を3D→2Dに変換する ConvertWorldLocationToScreenLocationノードは、スクリーン座標の限界を超えた値も返してくるので、Clampノードにスクリーン座標の最小値、最大値を設定してやります。

 このとき、追随するWidgetがはみ出さないないようもしたいので、そのぶん内側に制限を付けることになります。

 

f:id:hiyokosabrey:20190310145306p:plain

 

ViewportSize から 追随するWidgetの表示サイズの半分のサイズを内側に入れればよいということになります。改めてノードを見てみるとこうなってました。

f:id:hiyokosabrey:20190310145815p:plain

Local Desired Screen Location はアクターのスクリーン座標です。Viewportの左端は 0 から始まるので、 Min は 追随するWidgetの半分のサイズになります。Max はViewportサイズから 追随するWidgetの半分を引き算すれば求まります。

 

これで、画面内から絶対はみ出さない仕組みが実現できます。

 

 

ということで、ここからは、オマケの仕組みを組み込んでいきます。

f:id:hiyokosabrey:20190310154211p:plain

グラフを開いて、空いているところに、表示する値を受け取るためのカスタムイベントを準備します。

f:id:hiyokosabrey:20190310193804p:plain

カスタムイベントは Float型の ピンを追加します。

f:id:hiyokosabrey:20190310194218p:plain

 

数値に単位の文字「 m 」をくっつけたいので、FormatTextノードを使います。

このノードは、定型のテキストに変数を差し込むことが出る便利なノードです。

{ }でピン名を囲って使います。上の例では、{0}m と入力しています。⑤

 

過去記事にもう少し詳しく書いています。

limesode.hatenablog.com

 

UE4の単位は、Project Settings > Editor > Appearance で設定されています。 

f:id:hiyokosabrey:20190310194925p:plain

 デフォルトでは cm(センチメートル)に設定されています。

 今回 単位を m(メートル)にしたいので、100で割ります。⑥

 割り算の結果と FormatTextノードを直接つなぐことが可能なのですが、小数点の桁数を変更したいので、 ToText (Float)ノード を利用します。⑦

この ToTextノードは詳細設定ができます。

f:id:hiyokosabrey:20190310200846p:plain

Minimum Fractional Digits を 1 にすると、キリのいい値のときでも、0 を表示するようになります。

 

これでWidgetの編集は完了です。

コンパイルして閉じます。

 

次は、このWidgetに距離を調べて渡す部分。

レベルブループリントを編集します。

すでにつないである部分2つ目の Create Widget のReturn Value ピンで右クリックします。

f:id:hiyokosabrey:20190310205114p:plain

Remote To Variable (変数へ昇格)を選択して、変数化します。

f:id:hiyokosabrey:20190310205410p:plain

これに名前を付けて、間に挟み込みます。

f:id:hiyokosabrey:20190310205521p:plain

この操作で、追随するWidgetに対してアクセスが可能になります。

 

これをEventTickで使用します。

f:id:hiyokosabrey:20190310210459p:plain

対象の3Dオブジェクトと、プレイヤーカメラの距離を表示してみます。

双方のポジションを取得して引き算すると差分が得られます。それを VectorLength ノードが距離として返してくれるので、これを利用します。

 

 最初3次元上の2点の距離を調べる公式を使ってノードを組んでいたのですが、

f:id:hiyokosabrey:20190310212922p:plain

f:id:hiyokosabrey:20190310212023p:plain

ブループリントで数式を組むとなんだか不思議な感じがするのは気のせい?

 

検証している途中でステキな記事を見つけたので、シンプルな VectorLength を使った形に変えました。

qiita.com

 

 これで完成です。

 

 再生して確認してみると、

f:id:hiyokosabrey:20190310211607g:plain

ここまで来たらあとは見た目をゴニョゴニョするだけですね。

 

 

いかがだったでしょうか?

実際に組み込もうとすると、もっと機能性や汎用性を持たせた方がいいと思いますが、ゲームメカニクス次第なところもあってこれ以上は踏み込むのは難しいかな。

とはいえ結構個人的に面白いネタだと思うので、もう少し遊んでみようと考えています。

分かりにくいとことかあれば↓コメントかツイッターで質問OKです。

 

こんこんと泉のごとく沸き続ける鼻水と目のかゆみと闘いながら変なテンションで書いています。

いちいち眼鏡外さなくてもさりげなく点眼する方法って開発されないかな。

 

ではでは

ステキなマーカーライフを!