みつまめ杏仁

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

UMGの移動アニメーションを作る場合

UMGで移動アニメーションを作る場合、PositionTranslation の2種類があって、表示された見た目には違いがないように感じますが、それぞれ仕様が違うので私は使い分けて使っています。

今回はその使い分けについての私なりの解釈をぐだーと書いてみます。

 

下図は、UMGのキャンバスに置いたImageパーツのDetails(詳細)タブ。

f:id:hiyokosabrey:20171103113340j:plain

まず先に書いてしまうと、

  • Position は レイアウト用
  • Translation はアニメーション用

として捉えています。

実際に使ってみると判るのですが、ポイントは座標の扱い方が違うという点です。

Position は Anchor を基準としたキャンバスパネル内の絶対位置である「居場所」を指定します。一方 Translation は今いる場所からの プラスマイナスした位置という「移動量」を指定します。コトバの意味としても、辞書を見ると translate には「場所」をイメージさせるような訳がなく「翻訳」の他に「変形」という和訳がありました。

カテゴリも Render Transform ですしね。

なので、覚え方としては、

  • Position で指定した場所から Translation で指定した分だけ変形

となります。

「移動」と書かずにあえて「変形」と書いたのは、UMGでは移動も変形として扱っているからです。

3Dモデルで考えると解りやすいのですが、まずモデルには中心(Center)があって、そこから離れた位置に各頂点(Vertex)があります。この各頂点はモデルの中心からの相対位置でデータ化されています。Transform は形状を変形させることになり、変形することは頂点が移動することを意味します。例として頂点4個の四角いメッシュでイメージ図を作ってみました。

f:id:hiyokosabrey:20171103144901p:plain

f:id:hiyokosabrey:20171103144913p:plain

 UMGやFlashのような2DベースのGUI制作に慣れ親しんでいると、この「頂点」があるというイメージは感覚的に身に付けるのは難しいかもしれませんね。

 

ちなみに

キャンバス内でレイアウトを触っていると、値が変化するのは、Position の方で、Translation は、0.0 のままです。

 

 

ブループリントで位置を触る場合

位置を変えることができるノードは2つあります。

Position の方は Canvas Slot 型に キャストしてやる必要があります。

 

f:id:hiyokosabrey:20171103115727p:plain

Image_22 というのはUMGでテキトーに置いたImageパーツです。

 

変数には Canvas Panel Slot型というのがあるので、ブループリント内で繰り返しPosition を触る場合は、変数化しておくと便利です。

f:id:hiyokosabrey:20171103142255p:plain

 

で、結局のところ・・・

移動アニメーションは基本的に Translation で行うと扱いがラクになります。

UMGのキャンバスでのレイアウトの変更や調整がやりやすいからです。

 

例えばフレームインしてくるアニメーションの場合、まず最終表示位置にパーツを並べて位置を決定します。そのあとで、Translation でそこから表示開始位置まで移動するのをアニメーションキーで登録します。この時の値は、いくらか離れた位置を開始位置にして、終了位置は必ず 0.0 になります。開始位置のキーは、単に移動量を表わすことになりとてもシンプルです。

 

後で、レイアウト上の問題が発生してこの最終表示位置をキャンバス内で修正したとしても、調整レベルであればアニメーションキーの修正は必要ありません。これが結構メリットになります。

 

座標の扱いについてまとめると

  • Position は キャンバスに対しての 絶対座標
  • Translation相対座標 または Position からの オフセット位置

ということになります。

 

UMGのAnimationでは Translation で動きをつけ、ブループリント では Position を使って配置する、といった使い方が便利でよく使う手です。

 

 

 今回は以上です

 Position と Translation の使い分けで、迷っておられる方があれば、ぜひ参考になればうれしいです。

 

ではでは ステキな移動アニメーションライフを!

 

 

UMGのテキストを動かしてみる

UMGでテキストを表示します。キレイにアウトラインフォントが表示されます。

うん。

・・・・。

そう、ちょっと刺激が足りないなと、気づいたのです。

このままで終わっていいのか?テキストブロック!

お前の人生、ただ指示された通りの文字を並べるだけでいいのかよ!

と、中学校の文化祭に行って観劇してたら急にそんな気分になったのでした。

で、

テキストの文字を1文字ずつアニメーションさせてみようと思って遊んでみました。

 

テキトーにWidgetブループリントを用意します。

さっそくキャンバスに、Horizontal Box をひとつ。is Variable のチェックを忘れずに。

f:id:hiyokosabrey:20171020213815p:plain

あと、Size to Content にもチェックを付けます。

で、

ブループリント。

 

まず変数を3つ新しく用意します。

f:id:hiyokosabrey:20171020213710p:plain

上から、

Boolean型の変数は、準備できるまで、 Event Tickに我慢させるためのフラグ。

TextBlock型の配列変数は、文字をバラバラに管理するため。

Float型の変数は、サインカーブのための角度を保持するため。

 

変数が用意できたら、続けて関数を一つ。

関数にも、ローカル変数を3つ。これは関数内だけで利用される変数。

f:id:hiyokosabrey:20171020221553p:plain

上から、

String型の変数は、パラメータとして受け取った文字列を保持しておくため。

Slate Font Info型の変数は、フォントの設定を保持させておくため。

Integer型の変数は、受け取った文字列の長さを保持しておくため。

 

ちょっと画像が大きくなったので、左右で2枚になりました。

f:id:hiyokosabrey:20171020222152p:plain

文字列の長さを、 Lengthノードで調べると、単純に文字の数を調べてくれます。

例えば、"0123456789" のように数字が並んでいる場合、文字の数は10個です。

ForLoop ノードでは、繰り返しの回数を指定する場合に、Index番号という数値で管理します。なので、例えば回数を10にしたい場合、開始 Indexが 0 、終了 Index が 9 となります。

開始 Indexが 5 、 終了 Index が 14 の場合でも、ループ回数に変わりはないのです。

じゃあ、1から始めりゃいいじゃん、ってなるかと思うのですが、最近「ゼロから始める○○」が人気のヒケツ(?)っぽいので、まぁそんな感じです。(実際は座標なんかを計算したり配列を扱うときにゼロから始まっていた方が都合がいい場合が多いのです)

 

なので、ForLoopの繰り返し処理では、文字列の長さを、Last Indexとして使うためには 予め ー1しています。

 

変数の値を参照しにいくたびに 毎回 -1 の計算が入ってしまうのを避けるために、下のようにしています。

f:id:hiyokosabrey:20171020223655p:plain

ForLoopの中を見てみると、First Index は回数をカウントするためにローカル変数で管理されていますが、 Last Index の方は、毎回 つながったピンの値を参照します。

下のようにつないでしまうと、Last Indexピンが値を受け取ろうとするときに、-1の計算が入ってしまいます。

f:id:hiyokosabrey:20171020223825p:plain

この程度たかが知れてますが、普段から意識してないと「チリツモ」になりやすいので、エンジニアと良好な関係を築くために、UI紳士としては意識しないわけにはいかないと思うのです。

はい、流れを戻して続きを。

f:id:hiyokosabrey:20171020225505p:plain

左端のノードは、Construct Object from Class という名のノードです。

↓取り出した直後の状態。

f:id:hiyokosabrey:20171020225745p:plain

Classをセットすると、NONEの部分の表記が変わります。今回はテキストブロックなので、Text をチョイスします。

 

ローカル変数 TempFontの中身は、Details (詳細)タブで設定します。今回はこんな感じ。

f:id:hiyokosabrey:20171020230224p:plain

 

この関数では、テキストブロックをブループリントで生成しています。

そしていつもなら、クリックとキーボードで設定していた内容も、変数と専用のノードを組み合わせて設定しています。一文字づつ処理して、Horizontal Box に並べていくのです。

ひととおり並べ終わったら、ループ処理のあとに、ブーリアン型の変数に True をセットします。

これで関数は完成。

 

つぎにアニメーションの部分。Event Tick を利用します。

関数が呼ばれると、文字列がバラバラになって並べられて、最後に ブーリアン型の変数に True が入ってようやく、このEvent Tick が最後まで稼働します。

f:id:hiyokosabrey:20171020230926p:plain

Event Tickは、 このWidgetブループリントが Viewport に置かれたらすぐに動き出します。関数が呼ばれる前に動き出すとエラーが出るので、必要な処理が終わるまでは、Branchノードでブロックしています。

 %ノードは、 値がずぅーっと加算され続けるのを防ぐためと、円の1周分の角度は360度だからです。

角度から、三角関数を使って 値を計算します。その担当をマクロにしました。

f:id:hiyokosabrey:20171020233958p:plain

Period は カーブの周期(的なもの)、Radius は 半径 です。

中身はこうなってます。

f:id:hiyokosabrey:20171020234221p:plain

三角関数の角度Θ(シータ)にあたる部分は、「ラジアン」(Radians)が基本かと思いますが、ブループリントでは、0°から360°の「角度」(Degrees)で扱うノードも用意されています。

f:id:hiyokosabrey:20171020234801p:plain

マクロでは角度 Degrees の方を使用しています。

 

これで準備はできました。

あとは関数を呼び出して文字列を渡すだけです。

とりあえず確認のために、Event Construct につなぎます。

f:id:hiyokosabrey:20171020235150p:plain

 

これで Widgetは完成です。

 

 

あとはレベルブループリントにて、Viewportに追加するだけです。

f:id:hiyokosabrey:20171020235427p:plain

 

再生してみましょう。

 f:id:hiyokosabrey:20171021000449g:plain

 Screen to GIF というアプリを使ってみました。

ちょっとひっかかるときがあるけど、い感じに撮れてます。

サインカーブを、Y座標にするこんな風に上下にユラユラします。

 

マクロのパラメータをいろいろいじると揺れ具合が調整できます。

Period を大きな値にすると、カーブが細かくなってジグザグします。

Radiusの値を大きくすると上下の揺れ幅が大きくなります。

 

この値を、Shear や Angle にセットしても面白い動きになります。

f:id:hiyokosabrey:20171021001200p:plain

 

 Angleの場合。

f:id:hiyokosabrey:20171021001729p:plain

できたのが

f:id:hiyokosabrey:20171021001745g:plain

 

Shearの場合。

f:id:hiyokosabrey:20171021002328p:plain

動きは

f:id:hiyokosabrey:20171021002352g:plain

ちょっと酔いそう。

 

サインカーブは、プラスとマイナスの値を行ったり来たりするので、どちらか一方だけにするとバウンドするような動きになります。

f:id:hiyokosabrey:20171021003928p:plain

 すると、

f:id:hiyokosabrey:20171021020110g:plain

他にもカラーを変えてみたりもできます。

 

ロード画面くらいしか使える場所は無さそうですが、ちょっとは刺激的になった気がします。なかなか楽しいですねUE。

 

ではでは 今回はこの辺で。

 

 

 

 

話しかけるときなんかのマーカーみたいなやつを作ってみる【改】

前回の記事でポップアップする▼マーカーを作ってみたのですが、改造することにしました。

limesode.hatenablog.com

なぜかというと、このタイプのUI表示は同時に複数出ることがなく、常に一つだけが表示されます。前回の作り方では呼び出し元であるActorの配置とタイミング次第では、いくつもスポーンしてしまう可能性があったからです。複数個現れないようにきっちリと管理すればいいのですが、ちょっとフローが複雑になりそうなので思い切って作り変えることにしました。

どうやらプログラム界隈では「シングルトン パターン」なるものがあるようで、同じような感じの扱いができないかな? ということで試してみたのが今回の内容でもあります。

UE4にその仕組みが実装できるのかどうか、自分の頭では見つけられなかったので、完全に我流になります。

 

前回作った、BP_PopMarker をあらかじめワールドに置いておきます。座標は適宜書き換えることになるので、適当な位置でも大丈夫。

f:id:hiyokosabrey:20171017225130p:plain

このブループリントをさっそく改造します。

最初から表示されていない方がいいので Construction Script 内で非表示にします。

f:id:hiyokosabrey:20171017225748p:plain

Set Visibilityノードにある2つのチェックボックスのうち、

Propagate to Children にチェックを付けると、Targetの子階層もまとめて扱うことができます。この場合 Default Scene Root に対して設定しているので、このBlueprintに含まれる全てが対象になります。

 

次に、変数の設定を変えます。

f:id:hiyokosabrey:20171017230356p:plain

前回このName型の変数は、 Expose on Spawn にチェックを入れていました。

これを戻します。

f:id:hiyokosabrey:20171017230648p:plain

この変数は、値を受け取るという役割のはそのままです。

 

次に

Event Graph の Event BeginPlay につないだノードを編集します。

f:id:hiyokosabrey:20171017232016p:plain

 キャストノードの後ろの部分を消します。

そしてキャスト結果にピンから変数を昇格させて作ります。

f:id:hiyokosabrey:20171017232048p:plain

 

続けて、できたばかりのオリジナルWidget型の変数を利用するために、

新しくカスタムイベントを2つ追加します。

まずは表示開始のイベント。

f:id:hiyokosabrey:20171017230845p:plain

表示終了のイベント。

f:id:hiyokosabrey:20171017230854p:plain

用が済んだので変数の中身を空にしています。

 

これでポップするブループリントは完成です。

 

このポップアップマーカー▼の呼び出し元を変えます。

f:id:hiyokosabrey:20171017232910p:plain

このブループリント内で、コリジョンの判定をしていましたが、バッサリ止めます。

f:id:hiyokosabrey:20171017233341p:plain

あとは、変数化していたWidget型の変数を削除してお片付け完了です。

f:id:hiyokosabrey:20171017233627p:plain

スッキリしたところで保存します。

 

これで前回分の改造は終了です。ここからは、新しく追加する作業です。

 

レベルブループリントで

ワールドに置いた BP_PopMarker をレベルブループリントから扱います。

レベルブループリントを開くには、Blueprintsアイコンの横の▼メニューから。

f:id:hiyokosabrey:20171017234332p:plain

開いたら、ワールドアウトライナから、配置した BP_Chair をドラッグ&ドロップします。

f:id:hiyokosabrey:20171017235213p:plain

このノードからコリジョン判定のBindイベントノードを取り出します。

f:id:hiyokosabrey:20171017235915p:plain

Assign On Actor Begin Overlap を選択するとノードが2つ出てきます。

f:id:hiyokosabrey:20171018000239p:plain

このBind~ ノードに、Event BeginPlayノードと、他の BP_Chair をつなぎます。

f:id:hiyokosabrey:20171018000618p:plain

カスタムイベントに下図のようにつないでいきます。

f:id:hiyokosabrey:20171018001436p:plain

タグの有無を判定して存在していれば、ワールドに置いている BP_PopMarkerのポジションを変更して、中のカスタムイベントにタグを渡しています。

カスタムイベントからは、値を2つ受け取ることができます。気になる場合は、Print StringノードにつなぐとActor名が確認できます。Overlaped Actor はこの場合 BP_Chair になります。

 

同様に、コリジョンから出た時のイベントもつなぎます。今度は、

Assign On Actor End Overlap です。

f:id:hiyokosabrey:20171018002618p:plain

Begin Overlap と同じように他の BP_Chair もつなぎます。

f:id:hiyokosabrey:20171018002949p:plain

カスタムイベントには、BP_PopMarker の中のイベント(消去用)を呼び出しています。

これで完了です。

 

 

コンパイルして確認してみましょう。

f:id:hiyokosabrey:20171018004138j:plain

前回と比べて変わることは無いのですが・・・

 

BP_Chair に 追加していた判定のイベントがごっそり無くなったことで、複数を配置したときの容量が節約できています。

シングルトンパターンなんていう身に持て余した言葉を冒頭に掲げたものの、結局は大したことしてなくて、ワールドに直置きしてそれを利用しているだけという・・・

ブループリントインターフェイスも試してみたのですが、インスタンスをうまく経由させることができませんでした。むむむ。力不足。

 

ひとまずこの方法で、もう少し進めてみようと思います。

ツッコミ等あればコメントください。

 

ではでは

ステキにポップする▼ライフを!

 

 

補足

前回の記事を実践している前提で書いたので、端折っているところが かなりあります。

 もし今回の記事から読んで、やってみようと思われる方は、

前回の記事を進めていく中で、BP_Chair を作る際に Boxコリジョンを置くだけOKです。

 

 

 

話しかけるときなんかのマーカーみたいなやつを作ってみる

今回は、3DのRPGで、NPCに話しかける時なんかに、頭の上につくマーカーみたいなやつを作ってみます。

UIのプロトタイプ的に気軽に試そうと思うので、材料はエンジン内のものだけで作ります。またワールド内に複数配置するのを前提として「タグ」を利用してみます。

 

f:id:hiyokosabrey:20171006002127j:plain

 

新しいプロジェクトから

プロジェクトのテンプレを Third Person にしてスターターコンテンツありで始めます。

f:id:hiyokosabrey:20171004232538p:plain

f:id:hiyokosabrey:20171004231630j:plain

 

ポップするやつを用意

こんな▼三角形のマーカーとキャラ名という構成にします。

まずは名前表示用のWidget Blueprintから。名前は適当で。

f:id:hiyokosabrey:20171006192049p:plain

まずはキャンバスにテキストブロックを一つ配置します。

f:id:hiyokosabrey:20171006190653p:plain

アンカーは上端中央。

f:id:hiyokosabrey:20171006191023p:plain

アライメント X0.5 にするとポジション X0.0 でセンタリングされます。

f:id:hiyokosabrey:20171006191045p:plain

f:id:hiyokosabrey:20171006191205p:plain

isVariable にチェックを付けて、関数で中身を書き換えるようにします。

f:id:hiyokosabrey:20171006191540p:plain

関数のInputs(引数)は、String型やText型でも問題ないですが、値を渡すときのことを考えてここでName型からText型にキャストします。

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

次に、マーカーのブループリント。

Blueprint (Actor型)を一つ用意します。名前は適当で。

f:id:hiyokosabrey:20171006192347p:plain このブループリントがポップアップすることになります。

 

▼のキャラは、プリセットで用意されている Cone(円錐) を利用します。

f:id:hiyokosabrey:20171006192713p:plain

コンポーネントを追加して、サイズをイイ感じに変更します。

f:id:hiyokosabrey:20171006193003p:plain

Location の Zは高さです。基本上向き ▲ なので、Rotation X を回転させて ▼ にします。

ここで点滅させるためのマテリアルを用意します。

f:id:hiyokosabrey:20171006193348p:plain

光源処理はしないので Unlit です。

f:id:hiyokosabrey:20171006193546p:plain

 

これをブループリントの ▼ に適用します。

f:id:hiyokosabrey:20171006193835p:plain

 

次に、Widgetを追加します。検索すると見つけやすいです。

f:id:hiyokosabrey:20171006194245p:plain

Detailsタブで、作ってあったWidgetをセットします。

f:id:hiyokosabrey:20171006194629p:plain

Widgetのポジションは、▼ の少し上あたりにします。

 

キャラがの用意が整ったので、ブループリントを触っていきます。

変数を2つ用意します。一つは▼の回転用(Float型)、もうひとつはキャラ名(Name型)を受け取るようです。

f:id:hiyokosabrey:20171006194945p:plain

Name型の変数は、Expose on SpawnInstans Editable にチェックを付けておきます。

f:id:hiyokosabrey:20171006195246p:plain

Expose on Spawn にチェックをつけておくと、スポーンさせられた時点で値を受け取ることができます。

そこで Event BeginPlay でこの変数の中身をWidgetの中の関数に渡します。

f:id:hiyokosabrey:20171006195757p:plain

あとは▼をくるくる回してやります。

f:id:hiyokosabrey:20171006200208p:plain

SetRelativeRotation ノードは 引数の Rotate ピンを分解して使います。

上下逆さまにするために、X軸に固定値の 180 を。 Yは変化しなくていいので 0。

Z軸は Event Tick から出力される値をもらって利用しています。ただ値が小さすぎるので100倍しています。スピードを変えるならこの倍率を調整します。

再生中、永遠に加算され続けるので、 (剰余)ノードを入れて 0~360 で循環するようにしています。

右に続いているのは、キャラ名のWidgetが常にカメラの方を向く処理です。

f:id:hiyokosabrey:20171006200932p:plain

ひとまずこれでポップするやつは完成です。

このブループリントに出現の演出と、消える時の演出を加えるとより豪華になります。

 

NPCのブループリントを仕込む

つぎにNPCとなる、 Blueprint (Actor型)を一つ用意します。名前は適当で。

f:id:hiyokosabrey:20171004232411p:plain

作ったBlueprint は空っぽなので、何かオブジェクトを持たせます。

左上の +Add Componet ボタンでスタティックメッシュを呼び出します。

f:id:hiyokosabrey:20171004233852p:plain

コンポーネントにスタティックメッシュが追加できたら、それを選択して Details(詳細)タブから適当なスタティックメッシュを選択します。とりあえずあのよく見る椅子をチョイス。

f:id:hiyokosabrey:20171004233839p:plain

↓こうなります。

f:id:hiyokosabrey:20171004234113j:plain

ついでにこのモデル自身のコリジョン設定を変更しておきます。

DetailsタブにCollisionの項目があるので、そこの Collision Preset を Default にしておきます。

f:id:hiyokosabrey:20171004234634p:plain

 

もう一つ必要なコンポーネントがあります。マーカーを表示するためのトリガーとなるコリジョンです。

f:id:hiyokosabrey:20171004234941p:plain

Viewportで確認しながら、コリジョンのサイズと位置を調整します。

f:id:hiyokosabrey:20171004235238p:plain

 

この後イベントを仕込んでいきます。

専用のイベントノードを2つ使います。 

f:id:hiyokosabrey:20171006203143p:plain

これを取り出す方法が2つあって、一つはコンポーネントのリストで右クリックする方法。Add Event > Add On ~

f:id:hiyokosabrey:20171006202655p:plain

もう一つは、Detailsタブの下の方にあるEvent項目の緑のボタン。

f:id:hiyokosabrey:20171006203041p:plain

どちらも対象のコンポーネントを選択している状態で取り出すことになります。

 

イベントノードが取り出せたら、ノードをつないでいきます。

f:id:hiyokosabrey:20171006204620p:plain

自身にタグが設定されていれば、スポーンするという流れです。

右端は、Spawn Actor from Class ノードのReturn(戻り値)ピンから、Promote to Variable(変数へ昇格)したものです。

用が済んだら消す処理で使います。

f:id:hiyokosabrey:20171006204636p:plain

これで、準備完了です。

 

ワールドに置いていく

できたブループリントをワールドに配置していきます。

f:id:hiyokosabrey:20171006205158j:plain

置いたら、このNPC(椅子)のDetailsタブからタグを設定します。

タグを設定したいアクターを選んだ状態で、Actor 項目の Tags を探します。

f:id:hiyokosabrey:20171006205441p:plain

ここに名前を入れていきます。

f:id:hiyokosabrey:20171006205736p:plain

他のNPCにもいろんな名前をセットしていきます。

 

再生して動作を確かめます。

f:id:hiyokosabrey:20171006210349j:plain

 

 この方法の最大のメリットは、同じ共通のアセットでも、ワールドに配置してから自由にタグを使って情報管理できる点です。タグは配列なので複数の値を扱うことができます。文字で持たせることになりますが、セリフなどのテーブルを参照するためのIDを仕込んだり、ストーリーの進行などに応じたステータス情報なんかも持たせると便利だと思うのですがいかがでしょうか。

 

f:id:hiyokosabrey:20171006212108j:plain

 

 

今回はこの辺にしておきます。

ではでは ステキにポップする▼ライフを!

 

 

 

ちょっと言い訳とか

だんだん更新間隔が広がっていますな・・・。

いろいろネタを思いつくものの、最近エンジンを起動すると発熱がすごくて、扇風機をすぐ横に置いてるんだけど腕ばっかりが冷えてきて、気合を入れるためのなんかいい感じのBGMを探してるうちに結局寝落ち、みたいな日々が続いていました。

ブログの更新が、忙しい時のストレス発散になってたんだと思う。忙しさが一段落してちょっと時間にゆとりができた途端、滞ってた他のものを消化し始めて、気が付くともう神無月。あと3か月もしたら年が明けてるという・・・

世の中のゲームUIデザイナーの一助になれば、そしてそこからUE4のユーザーが増えて、UE4がUI開発の でふぁくとすたんだーど になればいいなぁ。という壮大な野望を夢見て始めたので、まだまだ頑張らねば。

スクロールテキストを作ってみる

 

久しぶりの更新です。大丈夫、まだ生きてます。

電光掲示板なんかでよくある、長さが足りなくてテキストが右から左へ流れるアレをUMGで作ってみました。ただ流れるだけではなく、端っこをフェードさせます。

f:id:hiyokosabrey:20170826003912p:plain

最近見なくなりましたが、HTML界ではマーキーというタグがあったの思い出しました。

 

まずはマテリアルから。

ちょっと前にヒストリアさんのブログでも紹介されてました ScreenPosition というのを使います。

 

ScreenPositionには ”ViewportUV” と ”SceneTextureUV” の2つの設定値があります。

f:id:hiyokosabrey:20170826090616p:plain

f:id:hiyokosabrey:20170826090628p:plain

公式のドキュメントには大した情報が載ってなくて、Tipsテキスト見ながら試してみたのですが、表示に差がありませんでした。

画面いっぱいサイズのWidgetに、試しに下のようなマテリアルをセットしてみると、

f:id:hiyokosabrey:20170826092128p:plain

U(水平)方向にグラデーションが現れます。

f:id:hiyokosabrey:20170826091929j:plain

ちゃんと調べて検証しないといけないですが、今回はとりあえず ViewportUV にして使ってみることにします。

 

Final Color に 白(Constant 3 Vector)をつないでいるのは、後からWidgetで好きなカラーにセットするためです。

 

というわけで、さっそく今回のスクロールテキスト用のマテリアルを用意します。

f:id:hiyokosabrey:20170826003921p:plain

 

 

キャンバスには、Horizontal Box をひとつ置きます。ブループリントから触るので isVariable にチェックを付けておきます。

f:id:hiyokosabrey:20170826004525p:plain

青い帯は、テキストを読みやすくするための下敷きです。

 

配置したHorizontal Box を動かすので、あらかじめ準備として Vector2D 型の変数を2つ用意してHorizontal Boxの初期位置を入れておきます。1つはポジションをリセットするのに使います。

f:id:hiyokosabrey:20170826005113p:plain

 

新幹線の車内ドアの上にある表示では、いくつかのニュースがつながって流れるので、同じように複数のテキストを一つにまとめる関数を用意しました。

 Text型の配列で結合する方法を見つけられなかったので、いったんString型のローカル変数に格納します。 ForEachLoopでText配列から一つずつ取り出して、String型にキャスト(型変換)しながら格納していきます。

f:id:hiyokosabrey:20170826101144p:plain

Text型の配列を全て、String型にして格納できたら、Join String Array ノードで一つにします。このとき間に挟み込む文字列(Separator)を指定できるので適当な記号やら文字を指定します。

関数内だけで完結して、関数の外に特に再利用されない変数はローカル変数にしておくといいです。ローカル変数にしておけば、この関数の処理が終わった後に内容が捨てられるので安全だからです。

 

 

 今回テキストのみですが、将来的にテキスト以外のもの(アイコンとか)も一緒にスクロールさせることを考えて、テキストブロックは動的に作ります。「動的に」というのは「その都度、必要に応じて」みたいなニュアンスです。

 

ブループリントから動的にいろんなオブジェクトを生みだすために

Construct Object from Class というノードを使います。

f:id:hiyokosabrey:20170826123123p:plain

取り出すと Construct NONE というノード名になるのでご注意を。

NONEの部分は Class によって自動的に変化します。下はTextBlockを設定した場合。

f:id:hiyokosabrey:20170826123415p:plain

 

TextBlockをブループリントで動的に生成するので、普段UMGエディタで設定していたものもブループリントから設定することになります。

そこで、フォントの設定をする関数を用意します。関数の 引数(Inputs)にTextBlock を設定します。

f:id:hiyokosabrey:20170826125048p:plain

テキストブロックに対しての設定を好きなだけ追加します。

f:id:hiyokosabrey:20170826124846p:plain

ここでは「色」と「フォント情報」をセットしています。テキストブロックでは、フォントの情報としてマテリアルを渡すことができるので、Make SlateFontInfo ノードの Font Material のところに 用意しておいた Screen Position を仕込んだマテリアルをセットします。

 

次にテキストをセットする関数を用意します。

ちょっと長いので左右に分けました。

f:id:hiyokosabrey:20170826124012p:plain

テキストを流し込んだら、キャンバスに置いておいたHorizontal Box の子供として追加します。

f:id:hiyokosabrey:20170826130026p:plain

右端のBoolean型の変数は、スクロール開始のためのフラグです。この関数が呼ばれたらテキストがセットされて画面に表示されるので、このタイミングでフラグを True にしています。

 

だいたい必要な材料が揃ったので、スクロールする処理を作ります。

EventGraph の EventTick を使います

これも長いので3つのパートに分けました。

EventTickはViewportに置かれたタイミング(Add to Viewport)で動き始めてしまうので、まずはスクロールするかしないかの判定をします。

f:id:hiyokosabrey:20170826131518p:plain

 Float型の変数を用意して、Delta Time を自身に加算していきます。

これは テキストが画面に表れていきなりスクロールすると読めないので、2秒待つようにするための待ち時間をチェックする変数です。

 

フラグが True になって、さらに2秒経ったら、スクロールし始めます。

EventTick によって、毎フレームこのノードたちが処理されるので、座標を加算してはHorizontalBoxのポジションを更新していきます。

f:id:hiyokosabrey:20170826132236p:plain

テキストの長さ(=HorizontalBoxのサイズ)分のスクロールが終わって画面から消えると、ポジションをリセットします。そのためにちょっと変な計算をしています。

f:id:hiyokosabrey:20170826133842p:plain

例えば、テキストの長さが 1000 だった場合、ポジションが -1000 になると画面から消えることになります。ここで問題になるのが、ポジションはマイナスの値。サイズはプラスの値ということです。符号を変える方法はいくつかあります。マイナスの値は ABSノードや -1 を掛けたりしてマイナスを打ち消すことができます。そこでポジションと長さを比較して判定すればいいのです。でも今回は足し算で済ませています。毎フレーム高速に処理するためには省略できるものはしましょう。

文字のサイズ的にピッタリ 0 にはほぼならないので、0以下のマイナスの値なったらスクロール終了ということにしています。

 

スクロールが終了したら、変数の値とポジションをリセットして次に備えます。

f:id:hiyokosabrey:20170826135256p:plain

 

これでWidgetは完成です。

実際に表示を確認してみます。

レベルブループリントに表示する仕組みを用意します。

いつもの Create Widget ノード から、Add to Viewport へのコンボ。

そこに、あとから中の関数を呼ぶためにReturn Value を変数化してものと、表示位置と表示サイズを設定するノードをつなげます。

f:id:hiyokosabrey:20170826202446p:plain

 仕上げに、テキストを渡す部分。

とりあえずスペースキーを押すと表示されるようにしました。

f:id:hiyokosabrey:20170826202830p:plain

 

完成です。

GIFにしてみました。コマ数が少ないので滑らかじゃないけど。

f:id:hiyokosabrey:20170826205454g:plain

 

これを商品レベルにするには、まだまだ必要な仕様やエラー対策なんかがありますが、ちょっとしたプロトタイプなら十分かなと思うのですがいかがでしょう。

 

ではでは

UI開発者の紳士淑女のみなさま

ステキなスクロールテキストライフを!

 

ついでに UIネタも募集中!

 

 

Widgetを3Dで扱うときに中の関数を呼び出す方法

いつのまにやらセミの声が凄まじい今日この頃、ふと前回の記事更新から随分空いていたことに気付きました。ようやくエディタのバージョン4.16をインストールしました。Widgetに新しいイベントが追加されてたんですよね。Widgetを3Dで扱うときに、EventConstruct の動作するタイミングが悩ましかったのですが、改善されていたらいいなと思いちょっとだけいじってみました。簡単な実験をしてみた結果、特に変わっていない印象です。また後日判ったことがあれば記事にしていきたいと思います。

 

さてさて今回はWidgetをWorld空間に3Dで表示する際に、中の関数やら変数にアクセスする方法をメモっておこうかと思います。

f:id:hiyokosabrey:20170723224553p:plain

Widgetアセットをプレイヤーや、背景と同じWorld空間を置く場合、2種類の置き方があります。Actorなどのブループリントに、Widgetコンポーネントとして持たせてしまう方法と、

f:id:hiyokosabrey:20170723224258p:plain

Add Widget Component ノードを使う方法です。

f:id:hiyokosabrey:20170723224007p:plain

どちらにせよ、どちらもWidgetコンポーネントという形になります。また、大抵ブループリントActorでくるむことになります。

ここでいう「くるむ」というのは、あくまでも本体はWidgetであって、Actorブループリントは3Dで置くための入れ物というイメージで使っている言葉です。

 

ただ置くだけならいいのですが、Widgetを置くからには大抵がUI表示のはず。そうなるとゲージやらテキストやらということになるので、内容のセットアップや更新に関数を使うことになるかと思います。その際にこのWidgetコンポーネントがちょっと面倒なのです。

Widgetを3Dにして扱うときは、このWidgetコンポーネントの状態になります。この状態だと中の関数や変数にサクッとアクセスできません。アクセスするにはキャストが必要になります。

このとき必ず必要なノードがあります。 Get User Widget Object ノードです。

一旦 UserWidget型 にした後、改めて本当の姿であるWidgetにキャストします。

下の図は、MyWidget3Dという名前のWidgetを用意した場合の例です。

 

まずは、Widgetがブループリント内のコンポーネントにしてある場合。

f:id:hiyokosabrey:20170723230138p:plain

つぎは、Add Widget Componentノードから追加した場合。

f:id:hiyokosabrey:20170723230157p:plain

キャストが成功したら、キャスト後の値を Promote to Variable(出力ピンの上で右クリック)で変数化しておくと便利です。以後その変数からドラッグすれば、中の関数や変数にアクセスできるようになるからです。

 

例)

f:id:hiyokosabrey:20170723233121p:plain

 

Widgetブループリントで作ったアセットを 3DのWorld空間に置こうとすると、WidgetComponentの状態で置かれる。

そのWidgetの中にアクセスするときは、いったんUserWidget型にしたあと、それぞれのWidget型にキャストしてやる必要があるということになります。

f:id:hiyokosabrey:20170725000413p:plain

ちょっと面倒ですが、この方法を使えば3DのUIも怖くない。と思うのですがいかがでしょう。

 

ではでは今回はこの辺で。

ステキな立体UIライフを!

 

 

 

 

キャラセレを作ってみる おまけ編

続きです。

シンプルな作りですが、キャラセレ画面としてある程度必要と思われる内容まで用意できました。今回はキャラを選ぶにあたっての能力値をグラフで表現したものを作って追加します。

f:id:hiyokosabrey:20170701213921j:plain

 

まずはテクスチャ。

f:id:hiyokosabrey:20170701081134p:plain

40x40 のグレースケールアルファチャンネルは無しです。

今回のゲージはマテリアルを使わずに増減させます。が、テクスチャ容量を節約したいので、ちょっぴりマテリアルの助けを借ります。

テクスチャのフォーマットをグレースケールにしてしまうとテクスチャのメモリ消費量は 1/4 に減らせられるのですが、UMGで扱えません。そこで下図のようなマテリアルを用意します。

f:id:hiyokosabrey:20170701214129p:plain

グレースケールなので、Rチャンネル(G、Bのピンでも結果は同じ)のピンをOpacityにつなぐことで、テクスチャをアルファチャンネルとして使えます。

TextureSampleノードの設定が変わります。

f:id:hiyokosabrey:20170701214335p:plain

(テクスチャ設定の sRGB のチェックを外すとLinearGrayscale になります)

マテリアルができたら、次はグラフ用のWidgetブループリントを新しく作ります。

 

作ったマテリアルを張り付けるImageパーツをキャンバスに配置します。

f:id:hiyokosabrey:20170701214909p:plain

このImageパーツにマテリアルをセットしたらヨコに伸ばして使うので設定をいじります。

f:id:hiyokosabrey:20170701215413p:plain

ひとまず横の長さは400にします。

このパーツは下敷きにするので、適当に暗めのカラーにします。

f:id:hiyokosabrey:20170701220458p:plain

このImageパーツを複製します。

複製したらカラーを明るくして上に重なるように移動して、さらにZOrderの数値を調整します。

f:id:hiyokosabrey:20170701220713p:plain

ゲージ部分のパーツが完成です。次にテキストブロックを左横に配置します。

f:id:hiyokosabrey:20170701220923p:plain

このテキストブロックはブループリントから内容を書き換えるので、 Is Variable にチェックを付けておきます。

f:id:hiyokosabrey:20170701221138p:plain

さて、肝心のゲージが増減するしくみですが、アニメーションを使います。

新しくアニメーションを作成して、ゲージのサイズをアニメーションさせます。

キーフレームは1秒刻みで、0.0~9.0 にキーを打ちます。動かすのは Slot のSize

f:id:hiyokosabrey:20170701222352p:plain  → f:id:hiyokosabrey:20170701222413p:plain

1秒あたり 40ずつ サイズを大きくしていきます。

f:id:hiyokosabrey:20170701222424p:plain

キーを打ち終えたら、

トラックのキーを全てConstantに変えます。マウスドラッグでアニメーションのキーを一気に選択したら、右クリックするとサブメニューが出てくるのでConstantを選択します。Constantはキーとキーの間を補間しません。

f:id:hiyokosabrey:20170701222741p:plain

Sizeの値をキーセーブしてるのに、トラックには Offset と書かれるのはちょっと混乱しそうです。

f:id:hiyokosabrey:20170701224351g:plain

 

アニメーションができたら、あとは、Widgetブループリントでコントロールします。

必要な関数は2つです。

 

名前をセットする関数

引数でテキストを受け取ってキャンバスに置いたテキストブロックにセットしてやります。

f:id:hiyokosabrey:20170701224735p:plain

 

 

 グラフの長さをセットする関数

引数でグラフの長さに対する値を受け取ってアニメーションのキーフレームに変換します。アニメーションの再生は Play Animation To を使います。

f:id:hiyokosabrey:20170701225008p:plain

このノードは再生する場所を指定できます。↑の Volume はUMGで作ったアニメーションコンポーネントの名前です。

StartとEndの値が同じ場合、アニメーションは動き続けるので、再生したらすぐに止めます。

この、指定した時間で止めてグラフの長さを調整する方法は Flash でアニメーションを制作したことがある方は、すでにお馴染みの方法だろうと思いますがいかがでしょうか?

 

2つの関数ができたところで、このゲージアセットは完成です。

 

 

実装してみる

セレクト画面のメインWidgeを開いて、できたゲージのWidgetをキャンバスに並べます。今回は4つ。ちょっと大きめの枠線は親のキャンバスパネルです。

f:id:hiyokosabrey:20170701230255j:plain

キャンバスパネルに子供として並べておくと、まとめて移動したり、表示のON・OFFが簡単にできるのでオススメです。キャンバスは親子構造にできるので便利ですが階層が深過ぎると処理負荷があがるのでほどほどに。

 キャンバスに並べたら、扱いやすくするために配列にしてしまいます。

Initialize関数を編集します。

f:id:hiyokosabrey:20170701231309p:plain

先に配列変数を用意しても問題ないですが、オススメの方法をご紹介します。

まずVariablesのリストに並んでいるWidgetを4つドロップします。→①

つぎに一番上のノードのピンからドラッグして Make Array ノードを取り出します。→ ②

Add pin でピンを増やし、ノードを4つ全部つないだら、Make ArrayノードのArrayピンの上で右クリックして Promote to Variable を選択します。 → ③

ゲージWidgetの型の配列変数ができあがります。

 

次にテキスト型の配列変数を用意します。

f:id:hiyokosabrey:20170701232211p:plain

これは変数の型が Text なので、探すのが簡単です。配列変数にしたら、Setでグラフにドロップして、MakeArrayノードをつなぎます。テキストはゲージの横に表示する名前になります。

 

さっそくゲージに名前をセットしてしまいましょう。続きにForEachLoop ノードをつなぎます。(キーボードの[F] を押しながらクリック)

下のようにつなぎます。

f:id:hiyokosabrey:20170701232924p:plain

この状態で再生してみると、このようになります。

f:id:hiyokosabrey:20170701233016p:plain

 

 

データテーブルを用意する

次に、キャラクターの能力値をまとめたデータテーブルを作ります。

まず先に用意するのはStructure

コンテンツブラウザで、右クリック > Blueprints > Structure

f:id:hiyokosabrey:20170702143612p:plain

開いて編集します。中の New Variable ボタンを押すと変数が追加できるので、Integer型の変数を4つ追加します。

f:id:hiyokosabrey:20170702143834p:plain

 Default Value(初期値)は0で構いません。

できたら保存して閉じます。

次に DataTable を用意します。これもコンテンツブラウザで

右クリック > Miscellaneous > Data Table

を選択。

f:id:hiyokosabrey:20170702144348p:plain

Structureを選べと訊いてくるので、作ったStructureを選んでOKボタンを押します。

f:id:hiyokosabrey:20170702145017p:plain

ダブルクリックして編集します。下のようなエディタが開くので下段のRowEditorタブにある + ボタンをクリックします → ①

f:id:hiyokosabrey:20170702144841p:plain

クリックしたらRow Name を変更します。 → ②

この名前はキャラクターのIndex番号になります。後でブループリントから扱うときに数字を使うので 0~7 の半角の数字にしておきます。

f:id:hiyokosabrey:20170702145431p:plain

キャラクター全員のパラメータを入力できたら保存して閉じます。

 

再びメインのキャラセレWidgetに戻ります。

関数を一つ新しく追加します。

作ったデータテーブルを読み込んで必要なデータを取り出すために、

Get Data Table Row ノードを使います。

f:id:hiyokosabrey:20170702150022p:plain

Data Tableの青いピンのところに用意しておいたData Table をセットします。

Row Name No薄い紫のピンには、Integer型の引数からString型を経由してつなぎます。

f:id:hiyokosabrey:20170702145746p:plain

Integer型 → String型 → Name型

こういった型を変換することをキャストすると言います。私たち人間は「 1 」という文字を見た時、「字」なのか「数」なのかを周辺の情報をもとに判断します。コンピュータはある意味単純なので型を指定してやらないと意図通りに処理してくれません。キャストにはルールがあってキャストできないものがあります。Int型(Integerは省略すると Int)は いきなりName型にできないので、いったんString型にします。

 

これで、RowNameに該当するデータの有無で右の処理が分岐します。青いピンからは見つかった場合にデータが取り出せます。

データを取り出したらBreake ノードで分解することができます。

f:id:hiyokosabrey:20170702152230p:plain

Initialize関数で準備したゲージの配列変数をここで使います。Getノードで順番に取り出しながら、ゲージのWidgetに用意しておいた関数に値を渡してやります。

 

Return Node で戻り値(Success=Boolean型)を設定しているのはデータが取り出せなかったときのエラー対策をとるための準備なので、べつに無くても問題ありません。

 

関数は出来上がりです。

これをEventGraphにつなぎます。場所は2か所。

f:id:hiyokosabrey:20170702152801p:plain

f:id:hiyokosabrey:20170702152850p:plain

最初のカーソル位置とサムネイルにフォーカスをセットする部分と、キー入力を受け付けてフォーカスを切り替える部分です。

 

出来上がりです。

 

f:id:hiyokosabrey:20170702153417j:plain

f:id:hiyokosabrey:20170702153426j:plain

 

いかがでしたか?

結構なボリュームになりましたが、だいたいの構造と必要な処理が理解できれば、絵素材の制作を除くと1時間もあれば作れるようになります。今回のオマケ部分はいろんな作り方があるので時間の掛かり方はアイデア次第になりますが、ぜひイロイロ凝ったものを試してみたいですね。

今回の作ってみるシリーズはあまり集中して書く時間が取れなかったので、間があいてしまったのが残念です。前半に比べ後半は細かい説明は省略してますので、記事の内容で分からないとこや間違いの指摘等あれば、お気軽にコメントください。

 

ではでは

ステキなキャラセレライフを!

 

 

追記:
今回のエントリー扱っている絵素材については、いらすとやさんの素材を使わせてもらいました。使用するにあたって事前にやり取りはしておりませんので問題あれば記事を削除いたします。