みつまめ杏仁

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

3Dオブジェクトの上にWidgetをマーカーのように表示する。画面外にいっても大丈夫。操作手順もできるだけ丁寧に書いてみたけどどうかな?

前回の更新からちょっと間が空きましたが、ぷちコン作品を作ってました。

で、先日とあるツイートに目がとまったのです。

 

 

 これは、Worldに存在する Actor の上に Widget を表示するという内容で、さらに対象の Actor が画面外に出ても、画面の端に張り付くようにするという 素晴らしいものです。

 3Dオブジェクトの上にWidgetを表示するという方法は、すでにブログ等で公開されているので、目新しさは無いですが、画面の外に出ないようにする工夫に心惹かれました。

  ツイ主の @ghosticgames さんにブログで説明してもよいかコメントで確認したら、"Sure! "(もちろん!)という返事を頂けたので、さっそく当ブログで説明させていただくことにしたわけです。質問箱からもブログのネタにどうかという提案(いい感じに解釈)もあってこれはもうやってやるしかないなと。

 

 

まずは、さっそくツイート画像を見ながら全く同じように試してみました。

その結果がこちら。

 (このツイート、「メディア」にはログとして残ってるけど「ツイート」には出てこなくなった・・・)

 

 基本的な原理はとてもシンプルです。さっそく他の方法はないかとアレコレ実験してみたんだけど、扱い方次第になると思うので、今回は手を加えずに教材として説明してみます。

 

 ツイートされてる画像とレイアウトや変数名などが若干違うものがありますが、基本のロジックは同じ構造です。基本的な動作を作った後でアレンジとして対象までの距離を表示するとこまでやろうと思います。

f:id:hiyokosabrey:20190306231002j:plain

今回はUMGやブループリント編集に慣れていない方でも作れるように操作方法をなるべく細かく書くようにしてみました。(作業の能率がいいので英語環境をオススメします)

 

 

 

用意するWidgetは2つ。

f:id:hiyokosabrey:20190306231618p:plain

 まずは1つ目のWidgetから

 

キャンバスだけのWidget

コンテンツブラウザから、 右クリック > User InterfaceWidget Blueprint

で作成できます。

 

できたらダブルクリックしてエディタを開きます。

エディタウィンドウ左にある、Hierarchy タブの中を確認すると、あらかじめ Canvas Panel_0 というのが置かれているので、クリックして選択。

f:id:hiyokosabrey:20190306233841p:plain


続いて右上の Is Variable というチェックボックスにチェックを付けます。

f:id:hiyokosabrey:20190306233045p:plain

付けることで、ブループリントからこの CanvasPanel_0 に対して直接いじることができるようになります。キャンバスパネル は UIパーツを描画するため使われます。

 

念のためキャンバスの描画サイズを確認。

f:id:hiyokosabrey:20190306234311p:plain

これはこのWidgetを、画面いっぱいまで使って描くよ、という意味合いです。

 

このWidgetブループリントはこれで完成です。

コンパイルして保存したら閉じてもOK.

f:id:hiyokosabrey:20190307002412p:plain

 

 

 

次は2つ目のWidget

 

Actorをフォロー(追随)するWidget

今度はキャンバスのサイズを指定します。

f:id:hiyokosabrey:20190307003540p:plain

Fill Screen と書かれている部分は、プルダウンメニューになっているので、Custom を選択します。

幅(Width)と高さ(Height)を指定できるようになるので表示したい適当なサイズ(単位はピクセル)を入力します。今回は100x100で作りました。

f:id:hiyokosabrey:20190307003734p:plain

 

次にキャンバスに、2つのパーツ Image と TextBlock を配置します。まずは Hierarchyパネル で ドラッグ&ドロップします。

f:id:hiyokosabrey:20190307004520p:plain

ドロップしたら、エディタウィンドウ右のDetails パネルで設定をいじっていきます。

 

 Image の方から。

Anchor(アンカー)はそのままでOK。

f:id:hiyokosabrey:20190307223852p:plain

キャンバスの中めいっぱいになるように、Sizeをキャンバスの大きさと同じ値を入力します。

あとは Detailsタブの Appearance > Tint で好きなカラーをセットしたら下敷きは完成。

 

次にTextBlock。

こちらもAnchorを設定します。

f:id:hiyokosabrey:20190307221700p:plain

中央揃えにしたいので、『Top-Center(勝手に命名)』 を選択。

パラメータは以下。

f:id:hiyokosabrey:20190307222529p:plain

テキストのカラーと文字サイズを決めて、レイアウトをセンタリングにします。

f:id:hiyokosabrey:20190307222916p:plain

あと、これもブループリントから内容を書き換えるので、Is Variable にチェックを付けておきます。

 

キャンバスがこんな感じになればOK。

f:id:hiyokosabrey:20190307222732p:plain

 

いよいよブループリントを編集します。

編集モードを切り替えます。

f:id:hiyokosabrey:20190307224355p:plain

 

切り替わったら、グラフの何もない所で、右クリックします。

f:id:hiyokosabrey:20190307224712p:plain

Add Custom Event を探します。Search のところで検索ワードを入力するか、一番上にAdd Event というカテゴリを開くと見つかります。(←英語環境)

見つけて選択するとノードが現れるので、

f:id:hiyokosabrey:20190307224914p:plain

InitFancyFollowingWidget と名付けておきます。

ここにパラメータを受け取るための 入力ピン を追加します。

このノードを選択したまま(オレンジの枠線をつけた状態)Detailsタブを見てみるとInputs という項目があるので、右にある + ボタンを 静かに2回クリック。

f:id:hiyokosabrey:20190307225733p:plain

適当な型の設定項目が2つ並ぶので、上からプルダウンを開けて、

Actor を探します。

f:id:hiyokosabrey:20190307230610p:plain

2つ目は

Canvas Panel Slotを探して選択します。

f:id:hiyokosabrey:20190307230832p:plain

下のようになればOK。

f:id:hiyokosabrey:20190307225945p:plain

PINの目的(役割)を後から思い出せるように、それぞれに名前を付けておきます。

これでグラフに置いたノードが変化します。

f:id:hiyokosabrey:20190307231151p:plain

次は変数を用意します。この青いピンからドラッグして作成します。

f:id:hiyokosabrey:20190307232229p:plainPromote to variable(変数へ昇格)を選ぶと、ピンと同じ型の変数が、SETノードの形で現れます。

f:id:hiyokosabrey:20190307232352p:plain

名前を付け直したら、2つ目のピンも同じようにして変数化します。

f:id:hiyokosabrey:20190307232719p:plain

これで、ターゲットとなる Actor と配置用の キャンバスパネルスロット にアクセスするための準備ができました。まだこの時点では中身はなくて空っぽな状態です。型が決まった器があるだけです。

変数化できたので、エディタウィンドウ左にある、Variables のリストに追加されているのが確認できます。そこから、グラフにノードとして取り出します。

f:id:hiyokosabrey:20190307235600p:plain

Ctrlキーを押しながらドロップすると、いきなり GET のカタチで取り出せます。

ちなみに Altキーだと、SETのカタチで取り出せます。

 

このノードの中身が入っているか確認してから処理するので、確認用のノード is Valid をつなぎます。

f:id:hiyokosabrey:20190308000317p:plain

なんらかの不具合や処理順などで受け取りを失敗することがあります。その時は中身が無効になることがあるので、このノードでチェックするのが安全です。今回は 両方が有効の場合のみ 処理したいのでAND(Boorean)を使います。これは論理式というやつで『 A かつ B 』という意味になります。両方のIsValid ノードの結果が true になってようやく true になります。

これらを、グラフに最初から置かれている赤いイベントノード、Event Tick ノードにつなぎます。

f:id:hiyokosabrey:20190308000109p:plain

 

右端は、表示位置をセットするノードです。変数CanvasSlotをもう一つGETで取り出すと、そこから安全に取り出せます。

f:id:hiyokosabrey:20190308001627p:plain

f:id:hiyokosabrey:20190308001745p:plain

仕上げに、ポジションを計算する関数を用意します。

今回の要の部分です。

 

座標計算用の関数

MyBlueprintタブの Functions のところにある +ボタンをクリックして新しい関数を準備していきます。

f:id:hiyokosabrey:20190308002405p:plain

 名前を ProjectWorldToScreenClamed としておきます。

f:id:hiyokosabrey:20190308231337p:plain

グラフが切り替わっているので、ノードをつないでいきます。ちょっと長めです。

 

まず、①Get Player Controller 、そのReturn Value からは、②Get Viewport Size

f:id:hiyokosabrey:20190308232618p:plain

 右にある2つの変数は ローカル変数です。この関数内だけという限定された範囲で使うためのものです。関数の外では利用できない変数です。別の見方をするとこの関数以外での利用価値が無い、とも言えます。今回一時的な計算のためだけに存在するのでローカル変数を使用します。

 作り方は、Get Viewport Size ノードの SizeX と SizeY のピンからドラッグ&ドロップするとポップアップメニューが出るので Promote to Local variable を選択すると簡単です。

f:id:hiyokosabrey:20190308233407p:plain

  エディタ左の MyBlueprintタブに、関数を編集している時にだけ現れる Local Variables であらかじめ用意しておくこともできます。

f:id:hiyokosabrey:20190308233822p:plain

ドラッグして作る方が、VariableType(変数の型)が確実に設定されるのでオススメ。

 

まず Viewport の大きさ(画面表示サイズ)を変数に入れておきます。

 

 

今度はこのWidgetのキャンバスに置いたパーツのサイズを取得して、これもローカル変数に取り置きします。

f:id:hiyokosabrey:20190309105044p:plain

 

まず①Get a reference to self ノードから始めます。

f:id:hiyokosabrey:20190309001902p:plain

Get Desired Size表示しようとしているサイズを返します。Self(自身)に対してつないでいるので、キャンバスに配置したすべてのパーツを含めた最終的な表示サイズのことを指します。

③は vector2d と Float を掛け算するノードです。ドラッグ&ドロップして  *アスタリスク)で検索すると見つかります。

f:id:hiyokosabrey:20190309002428p:plain

 

④Get Viewport Scale は、ディスプレイ解像度を 1 として、UE4実行時に実際に表示されているウィンドウの表示倍率を返してくれます。

f:id:hiyokosabrey:20190309105223p:plain

ディスプレイの解像度設定は公式ドキュメントにあるように設定ファイル "GameUserSettings.ini" に書かれています。

 

⑥は 複数の型で構成されているVector系のデータを分解してくれるノードです。Vector2D 型 は Float型のデータを2つ束ねています。主に XY座標を扱う場合などで使います。

⑦と⑧は これも ローカル変数に格納します。

 

 

 まだまだ続きます。

これはワールドにあるアクターの座標をScreen座標に変換します。

f:id:hiyokosabrey:20190309113742p:plain

 用意しておいたカスタムイベント InitFancyFollowWidgetで受け取っている変数オブジェクトを変数リストからドラッグ&ドロップして始めます。

f:id:hiyokosabrey:20190309114328p:plain

そこから② Get Actor Location でアクターのワールド座標を取得 → ちょっとだけ位置をずらします。

ConvertWorldLocationToScreenLocation というノードで、3次元のワールド座標を画面で見ている2次元のスクリーン座標に変換します。このノードは、先にGet Player Controller ノードを取り出しておくとスムーズに取り出せます。④→⑤

ここにアクターのポジションを渡してやります。

これをまたローカル変数に取り置きします。⑧、⑨

 

右端のは Returnノードです。 関数には基本このリターンノードを置きます。関数というからには何かしら計算や処理した結果があるので、この Return ノードから、外に出してやるのです。ノード検索キーワードに ピリオド 「 . 」 を入力するとすぐ見つかります。

このリターンノードには アウトプット用のピンを追加できます。リターンノードを選択して、Detailsタブから追加します。

f:id:hiyokosabrey:20190309120515p:plain

 

このリターンノードで返す値がこちら。

f:id:hiyokosabrey:20190309121102p:plain

取り置きしてきた座標やサイズの値をここで一気に計算します。

上段が X座標、下段がY座標用です。それぞれ Clampノードではみだしをカットしています。今回の一番重要な処理だと思います。

Integer型 と Float型 の引き算はそのままではできないので、Integer型をFloat型にキャスト(型変換)してやります。

ノード検索で「 to 」と入力すると ToFloat(Integer) が選択できます。

f:id:hiyokosabrey:20190309122206p:plain

こんなノードが出てきます。

f:id:hiyokosabrey:20190309122432p:plain

全体図はこんな感じ。

f:id:hiyokosabrey:20190309122909p:plain

@ghosticgamesさんのと同じ内容ですが、説明用にコンパクトにしました。

 

これで関数は完成ですが、最後の仕上げとして、この関数をPure型にします。

f:id:hiyokosabrey:20190309125648p:plain

Pureにチェックを付けると・・・

f:id:hiyokosabrey:20190309130113p:plain

グラフにノードとして取り出した際に、実行ピン(白色)がない状態になります。

これを途中だった EventTick処理につないだら完成です。

f:id:hiyokosabrey:20190309130359p:plain

コンパイルして保存したらと閉じてもOK。

 

表示を確認してみる

ワールドに適当なオブジェクトを配置します。

f:id:hiyokosabrey:20190309131522j:plain

実験では、Transformを持ってさえいれば動作すると思います。

配置できたら、手っ取り早いのでレベルブループリントを使って確認します。

 

レベルブループリントの編集方法はこちらから。

f:id:hiyokosabrey:20190309131921j:plain

グラフ編集エディタが開きます。

 ①Create Widget からつないでいきます。

f:id:hiyokosabrey:20190309173918p:plain

アクターに追随するWidgetは、Viewort に追加せずに、先にAdd to Viewport したキャンバスだけのWidget内に持っている CanvasPanel の子供にします。

さらに続き、

f:id:hiyokosabrey:20190309174710p:plain

スムーズにつなげるコツは、白い線は気にせずに、Return Value からドラッグして検索することです。つながるのが約束されているノードが取り出せるので安心です。

 

⑧Set Auto Sizeノードは Inb Auto Size にチェックを付けておきます。

 

⑩はワールドに置いたオブジェクトをドラッグします。
f:id:hiyokosabrey:20190309180355p:plain

この方法は可能なブループリントが限られます。

 

これですべての準備は整いました。

再生してみましょう。適当にカメラを動かしてみてください。

f:id:hiyokosabrey:20190309182035j:plain

対象のオブジェクトが画面から消えても残り続けます。

f:id:hiyokosabrey:20190309182308j:plain

これが Clampを使った移動制限です。

 

一応動画のURLを貼っておきます。

みつまめ杏仁Ver

@ghosticgames さん版

 

細かくオペレーションを書いていたら思ったより長くなってしまいましたが、今回はこの辺まででいったん筆を置こうと思います。

 

次回、計算の仕組みを軽く解説して、オマケの「距離」を表示する部分を書きます。

@ghosticgames さん ステキな教材をありがとう。

 

ではでは

ステキなターゲット表示ライフを!