UMGのテキストを動かしてみる
UMGでテキストを表示します。キレイにアウトラインフォントが表示されます。
うん。
・・・・。
そう、ちょっと刺激が足りないなと、気づいたのです。
このままで終わっていいのか?テキストブロック!
お前の人生、ただ指示された通りの文字を並べるだけでいいのかよ!
と、中学校の文化祭に行って観劇してたら急にそんな気分になったのでした。
で、
テキストの文字を1文字ずつアニメーションさせてみようと思って遊んでみました。
テキトーにWidgetブループリントを用意します。
さっそくキャンバスに、Horizontal Box をひとつ。is Variable のチェックを忘れずに。
あと、Size to Content にもチェックを付けます。
で、
ブループリント。
まず変数を3つ新しく用意します。
上から、
Boolean型の変数は、準備できるまで、 Event Tickに我慢させるためのフラグ。
TextBlock型の配列変数は、文字をバラバラに管理するため。
Float型の変数は、サインカーブのための角度を保持するため。
変数が用意できたら、続けて関数を一つ。
関数にも、ローカル変数を3つ。これは関数内だけで利用される変数。
上から、
String型の変数は、パラメータとして受け取った文字列を保持しておくため。
Slate Font Info型の変数は、フォントの設定を保持させておくため。
Integer型の変数は、受け取った文字列の長さを保持しておくため。
ちょっと画像が大きくなったので、左右で2枚になりました。
文字列の長さを、 Lengthノードで調べると、単純に文字の数を調べてくれます。
例えば、"0123456789" のように数字が並んでいる場合、文字の数は10個です。
ForLoop ノードでは、繰り返しの回数を指定する場合に、Index番号という数値で管理します。なので、例えば回数を10にしたい場合、開始 Indexが 0 、終了 Index が 9 となります。
開始 Indexが 5 、 終了 Index が 14 の場合でも、ループ回数に変わりはないのです。
じゃあ、1から始めりゃいいじゃん、ってなるかと思うのですが、最近「ゼロから始める○○」が人気のヒケツ(?)っぽいので、まぁそんな感じです。(実際は座標なんかを計算したり配列を扱うときにゼロから始まっていた方が都合がいい場合が多いのです)
なので、ForLoopの繰り返し処理では、文字列の長さを、Last Indexとして使うためには 予め ー1しています。
変数の値を参照しにいくたびに 毎回 -1 の計算が入ってしまうのを避けるために、下のようにしています。
ForLoopの中を見てみると、First Index は回数をカウントするためにローカル変数で管理されていますが、 Last Index の方は、毎回 つながったピンの値を参照します。
下のようにつないでしまうと、Last Indexピンが値を受け取ろうとするときに、-1の計算が入ってしまいます。
この程度たかが知れてますが、普段から意識してないと「チリツモ」になりやすいので、エンジニアと良好な関係を築くために、UI紳士としては意識しないわけにはいかないと思うのです。
はい、流れを戻して続きを。
左端のノードは、Construct Object from Class という名のノードです。
↓取り出した直後の状態。
Classをセットすると、NONEの部分の表記が変わります。今回はテキストブロックなので、Text をチョイスします。
ローカル変数 TempFontの中身は、Details (詳細)タブで設定します。今回はこんな感じ。
この関数では、テキストブロックをブループリントで生成しています。
そしていつもなら、クリックとキーボードで設定していた内容も、変数と専用のノードを組み合わせて設定しています。一文字づつ処理して、Horizontal Box に並べていくのです。
ひととおり並べ終わったら、ループ処理のあとに、ブーリアン型の変数に True をセットします。
これで関数は完成。
つぎにアニメーションの部分。Event Tick を利用します。
関数が呼ばれると、文字列がバラバラになって並べられて、最後に ブーリアン型の変数に True が入ってようやく、このEvent Tick が最後まで稼働します。
Event Tickは、 このWidgetブループリントが Viewport に置かれたらすぐに動き出します。関数が呼ばれる前に動き出すとエラーが出るので、必要な処理が終わるまでは、Branchノードでブロックしています。
%ノードは、 値がずぅーっと加算され続けるのを防ぐためと、円の1周分の角度は360度だからです。
角度から、三角関数を使って 値を計算します。その担当をマクロにしました。
Period は カーブの周期(的なもの)、Radius は 半径 です。
中身はこうなってます。
三角関数の角度Θ(シータ)にあたる部分は、「ラジアン」(Radians)が基本かと思いますが、ブループリントでは、0°から360°の「角度」(Degrees)で扱うノードも用意されています。
マクロでは角度 Degrees の方を使用しています。
これで準備はできました。
あとは関数を呼び出して文字列を渡すだけです。
とりあえず確認のために、Event Construct につなぎます。
これで Widgetは完成です。
あとはレベルブループリントにて、Viewportに追加するだけです。
再生してみましょう。
Screen to GIF というアプリを使ってみました。
ちょっとひっかかるときがあるけど、い感じに撮れてます。
サインカーブを、Y座標にするこんな風に上下にユラユラします。
マクロのパラメータをいろいろいじると揺れ具合が調整できます。
Period を大きな値にすると、カーブが細かくなってジグザグします。
Radiusの値を大きくすると上下の揺れ幅が大きくなります。
この値を、Shear や Angle にセットしても面白い動きになります。
Angleの場合。
できたのが
Shearの場合。
動きは
ちょっと酔いそう。
サインカーブは、プラスとマイナスの値を行ったり来たりするので、どちらか一方だけにするとバウンドするような動きになります。
すると、
他にもカラーを変えてみたりもできます。
ロード画面くらいしか使える場所は無さそうですが、ちょっとは刺激的になった気がします。なかなか楽しいですねUE。
ではでは 今回はこの辺で。
話しかけるときなんかのマーカーみたいなやつを作ってみる【改】
前回の記事でポップアップする▼マーカーを作ってみたのですが、改造することにしました。
なぜかというと、このタイプのUI表示は同時に複数出ることがなく、常に一つだけが表示されます。前回の作り方では呼び出し元であるActorの配置とタイミング次第では、いくつもスポーンしてしまう可能性があったからです。複数個現れないようにきっちリと管理すればいいのですが、ちょっとフローが複雑になりそうなので思い切って作り変えることにしました。
どうやらプログラム界隈では「シングルトン パターン」なるものがあるようで、同じような感じの扱いができないかな? ということで試してみたのが今回の内容でもあります。
UE4にその仕組みが実装できるのかどうか、自分の頭では見つけられなかったので、完全に我流になります。
前回作った、BP_PopMarker をあらかじめワールドに置いておきます。座標は適宜書き換えることになるので、適当な位置でも大丈夫。
このブループリントをさっそく改造します。
最初から表示されていない方がいいので Construction Script 内で非表示にします。
Set Visibilityノードにある2つのチェックボックスのうち、
Propagate to Children にチェックを付けると、Targetの子階層もまとめて扱うことができます。この場合 Default Scene Root に対して設定しているので、このBlueprintに含まれる全てが対象になります。
次に、変数の設定を変えます。
前回このName型の変数は、 Expose on Spawn にチェックを入れていました。
これを戻します。
この変数は、値を受け取るという役割のはそのままです。
次に
Event Graph の Event BeginPlay につないだノードを編集します。
キャストノードの後ろの部分を消します。
そしてキャスト結果にピンから変数を昇格させて作ります。
続けて、できたばかりのオリジナルWidget型の変数を利用するために、
新しくカスタムイベントを2つ追加します。
まずは表示開始のイベント。
表示終了のイベント。
用が済んだので変数の中身を空にしています。
これでポップするブループリントは完成です。
このポップアップマーカー▼の呼び出し元を変えます。
このブループリント内で、コリジョンの判定をしていましたが、バッサリ止めます。
あとは、変数化していたWidget型の変数を削除してお片付け完了です。
スッキリしたところで保存します。
これで前回分の改造は終了です。ここからは、新しく追加する作業です。
レベルブループリントで
ワールドに置いた BP_PopMarker をレベルブループリントから扱います。
レベルブループリントを開くには、Blueprintsアイコンの横の▼メニューから。
開いたら、ワールドアウトライナから、配置した BP_Chair をドラッグ&ドロップします。
このノードからコリジョン判定のBindイベントノードを取り出します。
Assign On Actor Begin Overlap を選択するとノードが2つ出てきます。
このBind~ ノードに、Event BeginPlayノードと、他の BP_Chair をつなぎます。
カスタムイベントに下図のようにつないでいきます。
タグの有無を判定して存在していれば、ワールドに置いている BP_PopMarkerのポジションを変更して、中のカスタムイベントにタグを渡しています。
カスタムイベントからは、値を2つ受け取ることができます。気になる場合は、Print StringノードにつなぐとActor名が確認できます。Overlaped Actor はこの場合 BP_Chair になります。
同様に、コリジョンから出た時のイベントもつなぎます。今度は、
Assign On Actor End Overlap です。
Begin Overlap と同じように他の BP_Chair もつなぎます。
カスタムイベントには、BP_PopMarker の中のイベント(消去用)を呼び出しています。
これで完了です。
コンパイルして確認してみましょう。
前回と比べて変わることは無いのですが・・・
BP_Chair に 追加していた判定のイベントがごっそり無くなったことで、複数を配置したときの容量が節約できています。
シングルトンパターンなんていう身に持て余した言葉を冒頭に掲げたものの、結局は大したことしてなくて、ワールドに直置きしてそれを利用しているだけという・・・
ブループリントインターフェイスも試してみたのですが、インスタンスをうまく経由させることができませんでした。むむむ。力不足。
ひとまずこの方法で、もう少し進めてみようと思います。
ツッコミ等あればコメントください。
ではでは
ステキにポップする▼ライフを!
補足
前回の記事を実践している前提で書いたので、端折っているところが かなりあります。
もし今回の記事から読んで、やってみようと思われる方は、
前回の記事を進めていく中で、BP_Chair を作る際に Boxコリジョンを置くだけOKです。
話しかけるときなんかのマーカーみたいなやつを作ってみる
※ この記事は 内容に誤りがあったため 内容を一部加筆修正しています。 Ver 4.17で検証・作成しています(2019/4/25)
今回は、3DのRPGで、NPCに話しかける時なんかに、頭の上につくマーカーみたいなやつを作ってみます。
UIのプロトタイプ的に気軽に試そうと思うので、材料はエンジン内のものだけで作ります。またワールド内に複数配置するのを前提として「タグ」を利用してみます。
新しいプロジェクトから
プロジェクトのテンプレを Third Person にしてスターターコンテンツありで始めます。
ポップするやつを用意
こんな▼三角形のマーカーとキャラ名という構成にします。
まずは名前表示用のWidget Blueprintから。名前は適当で。
まずはキャンバスにテキストブロックを一つ配置します。
アンカーは上端中央。
アライメント X を 0.5 にするとポジション X が 0.0 でセンタリングされます。
isVariable にチェックを付けて、関数で中身を書き換えるようにします。
関数のInputs(引数)は、String型やText型でも問題ないですが、値を渡すときのことを考えてここでName型からText型にキャストします。
Widgetブループリントはここで完成です。
次に、マーカーのブループリント。
Blueprint (Actor型)を一つ用意します。名前は適当で。
このブループリントがポップアップすることになります。
▼のキャラは、プリセットで用意されている Cone(円錐) を利用します。
コンポーネントを追加して、サイズをイイ感じに変更します。
Location の Zは高さです。基本上向き ▲ なので、Rotation X を回転させて ▼ にします。
ここで点滅させるためのマテリアルを用意します。
光源処理はしないので Unlit です。
これを ▼ に適用します。エディタのDtailes(詳細)パネルにあるMaterials です。
続けてコリジョンの設定を変更します。
Generate Overlap Events のチェックを外すだけでもいいのですが、このマーカー自体コリジョンは不要なので、Collision Presets を No Collision にしておきます。
次に、Widgetを追加します。検索すると見つけやすいです。
Detailsタブで、作ってあったWidgetをセットします。
Widgetのポジションは、▼ の少し上あたりにします。
Widgetにもコリジョンの設定があるので、▼同様に外しておきます。
キャラがの用意が整ったので、ブループリントを触っていきます。
まず変数を2つ用意します。一つは▼の回転用(Float型)、もうひとつはキャラ名(Name型)を受け取るようです。
Name型の変数は、Expose on Spawn と Instans Editable にチェックを付けておきます。
Expose on Spawn にチェックをつけておくと、スポーンさせられた時点で値を受け取ることができます。
そこで Event BeginPlay でこの変数の中身をWidgetの中の関数に渡します。
あとは▼をくるくる回してやります。
SetRelativeRotation ノードは 引数の Rotate ピンを分解して使います。
上下逆さまにするために、X軸に固定値の 180 を。 Yは変化しなくていいので 0。
Z軸は Event Tick から出力される値をもらって利用しています。ただ値が小さすぎるので100倍しています。スピードを変えるならこの倍率を調整します。
再生中、永遠に加算され続けるので、 %(剰余)ノードを入れて 0~360 で循環するようにしています。
右に続いているのは、キャラ名のWidgetが常にカメラの方を向く処理です。
ひとまずこれでポップするやつは完成です。
このブループリントに出現の演出と、消える時の演出を加えるとより豪華になります。
NPCのブループリントを仕込む
つぎにNPCとなる、 Blueprint (Actor型)を一つ用意します。名前は適当で。
作ったBlueprint は空っぽなので、何かオブジェクトを持たせます。
左上の +Add Componet ボタンでスタティックメッシュを呼び出します。
コンポーネントにスタティックメッシュが追加できたら、それを選択して Details(詳細)タブから適当なスタティックメッシュを選択します。とりあえずあのよく見る椅子をチョイス。
↓こうなります。
ついでにこのモデル自身のコリジョン設定を変更しておきます。
DetailsタブにCollisionの項目があるので、そこの Collision Preset を Default にしておきます。
もう一つ必要なコンポーネントがあります。マーカーを表示するためのトリガーとなるコリジョンです。
Viewportで確認しながら、コリジョンのサイズと位置を調整します。
この後イベントを仕込んでいきます。
専用のイベントノードを2つ使います。
これを取り出す方法が2つあって、一つはコンポーネントのリストで右クリックする方法。Add Event > Add On ~
もう一つは、Detailsタブの下の方にあるEvent項目の緑のボタン。
どちらも対象のコンポーネントを選択している状態で取り出すことになります。
イベントノードが取り出せたら、ノードをつないでいきます。
まずは、On Component Begin Overlap イベントから。
自身にタグが設定されていれば、▼をスポーンするという流れです。
右端は、Spawn Actor from Class ノードのReturn(戻り値)ピンから、Promote to Variable(変数へ昇格)したものです。
この変数化したものは用が済んだら消す処理で使います。
で、さっそく On Component End Overlap イベントで使います。
コリジョンから出たらこのイベントが発動して、スポーンされていたマーカーは消えます。
スポーンしてなくても、このイベントは発動するので、Destroyする対象が無いということで、Warning と Error が Outputログに吐き出されてしまいます。中身が有効かどうかを、 Is Valid ノードで調べています。
これで、準備完了です。
ワールドに置いていく
できたブループリントをワールドに配置していきます。
置いたら、このNPC(椅子)のDetailsタブからタグを設定します。
タグを設定したいアクターを選んだ状態で、Actor 項目の Tags を探します。
ここに名前を入れていきます。
他のNPCにもいろんな名前をセットしていきます。
再生して動作を確かめます。
この方法の最大のメリットは、同じ共通のアセットでも、ワールドに配置してから自由にタグを使って情報管理できる点です。タグは配列なので複数の値を扱うことができます。文字で持たせることになりますが、セリフなどのテーブルを参照するためのIDを仕込んだり、ストーリーの進行などに応じたステータス情報なんかも持たせると便利だと思うのですがいかがでしょうか。
今回はこの辺にしておきます。
ではでは ステキにポップする▼ライフを!
ちょっと言い訳とか
だんだん更新間隔が広がっていますな・・・。
いろいろネタを思いつくものの、最近エンジンを起動すると発熱がすごくて、扇風機をすぐ横に置いてるんだけど腕ばっかりが冷えてきて、気合を入れるためのなんかいい感じのBGMを探してるうちに結局寝落ち、みたいな日々が続いていました。
ブログの更新が、忙しい時のストレス発散になってたんだと思う。忙しさが一段落してちょっと時間にゆとりができた途端、滞ってた他のものを消化し始めて、気が付くともう神無月。あと3か月もしたら年が明けてるという・・・
世の中のゲームUIデザイナーの一助になれば、そしてそこからUE4のユーザーが増えて、UE4がUI開発の でふぁくとすたんだーど になればいいなぁ。という壮大な野望を夢見て始めたので、まだまだ頑張らねば。
スクロールテキストを作ってみる
久しぶりの更新です。大丈夫、まだ生きてます。
電光掲示板なんかでよくある、長さが足りなくてテキストが右から左へ流れるアレをUMGで作ってみました。ただ流れるだけではなく、端っこをフェードさせます。
最近見なくなりましたが、HTML界ではマーキーというタグがあったの思い出しました。
まずはマテリアルから。
ちょっと前にヒストリアさんのブログでも紹介されてました ScreenPosition というのを使います。
ScreenPositionには ”ViewportUV” と ”SceneTextureUV” の2つの設定値があります。
公式のドキュメントには大した情報が載ってなくて、Tipsテキスト見ながら試してみたのですが、表示に差がありませんでした。
画面いっぱいサイズのWidgetに、試しに下のようなマテリアルをセットしてみると、
U(水平)方向にグラデーションが現れます。
ちゃんと調べて検証しないといけないですが、今回はとりあえず ViewportUV にして使ってみることにします。
Final Color に 白(Constant 3 Vector)をつないでいるのは、後からWidgetで好きなカラーにセットするためです。
というわけで、さっそく今回のスクロールテキスト用のマテリアルを用意します。
キャンバスには、Horizontal Box をひとつ置きます。ブループリントから触るので isVariable にチェックを付けておきます。
青い帯は、テキストを読みやすくするための下敷きです。
配置したHorizontal Box を動かすので、あらかじめ準備として Vector2D 型の変数を2つ用意してHorizontal Boxの初期位置を入れておきます。1つはポジションをリセットするのに使います。
新幹線の車内ドアの上にある表示では、いくつかのニュースがつながって流れるので、同じように複数のテキストを一つにまとめる関数を用意しました。
Text型の配列で結合する方法を見つけられなかったので、いったんString型のローカル変数に格納します。 ForEachLoopでText配列から一つずつ取り出して、String型にキャスト(型変換)しながら格納していきます。
Text型の配列を全て、String型にして格納できたら、Join String Array ノードで一つにします。このとき間に挟み込む文字列(Separator)を指定できるので適当な記号やら文字を指定します。
関数内だけで完結して、関数の外に特に再利用されない変数はローカル変数にしておくといいです。ローカル変数にしておけば、この関数の処理が終わった後に内容が捨てられるので安全だからです。
今回テキストのみですが、将来的にテキスト以外のもの(アイコンとか)も一緒にスクロールさせることを考えて、テキストブロックは動的に作ります。「動的に」というのは「その都度、必要に応じて」みたいなニュアンスです。
ブループリントから動的にいろんなオブジェクトを生みだすために
Construct Object from Class というノードを使います。
取り出すと Construct NONE というノード名になるのでご注意を。
NONEの部分は Class によって自動的に変化します。下はTextBlockを設定した場合。
TextBlockをブループリントで動的に生成するので、普段UMGエディタで設定していたものもブループリントから設定することになります。
そこで、フォントの設定をする関数を用意します。関数の 引数(Inputs)にTextBlock を設定します。
テキストブロックに対しての設定を好きなだけ追加します。
ここでは「色」と「フォント情報」をセットしています。テキストブロックでは、フォントの情報としてマテリアルを渡すことができるので、Make SlateFontInfo ノードの Font Material のところに 用意しておいた Screen Position を仕込んだマテリアルをセットします。
次にテキストをセットする関数を用意します。
ちょっと長いので左右に分けました。
テキストを流し込んだら、キャンバスに置いておいたHorizontal Box の子供として追加します。
右端のBoolean型の変数は、スクロール開始のためのフラグです。この関数が呼ばれたらテキストがセットされて画面に表示されるので、このタイミングでフラグを True にしています。
だいたい必要な材料が揃ったので、スクロールする処理を作ります。
EventGraph の EventTick を使います
これも長いので3つのパートに分けました。
EventTickはViewportに置かれたタイミング(Add to Viewport)で動き始めてしまうので、まずはスクロールするかしないかの判定をします。
Float型の変数を用意して、Delta Time を自身に加算していきます。
これは テキストが画面に表れていきなりスクロールすると読めないので、2秒待つようにするための待ち時間をチェックする変数です。
フラグが True になって、さらに2秒経ったら、スクロールし始めます。
EventTick によって、毎フレームこのノードたちが処理されるので、座標を加算してはHorizontalBoxのポジションを更新していきます。
テキストの長さ(=HorizontalBoxのサイズ)分のスクロールが終わって画面から消えると、ポジションをリセットします。そのためにちょっと変な計算をしています。
例えば、テキストの長さが 1000 だった場合、ポジションが -1000 になると画面から消えることになります。ここで問題になるのが、ポジションはマイナスの値。サイズはプラスの値ということです。符号を変える方法はいくつかあります。マイナスの値は ABSノードや -1 を掛けたりしてマイナスを打ち消すことができます。そこでポジションと長さを比較して判定すればいいのです。でも今回は足し算で済ませています。毎フレーム高速に処理するためには省略できるものはしましょう。
文字のサイズ的にピッタリ 0 にはほぼならないので、0以下のマイナスの値なったらスクロール終了ということにしています。
スクロールが終了したら、変数の値とポジションをリセットして次に備えます。
これでWidgetは完成です。
実際に表示を確認してみます。
レベルブループリントに表示する仕組みを用意します。
いつもの Create Widget ノード から、Add to Viewport へのコンボ。
そこに、あとから中の関数を呼ぶためにReturn Value を変数化してものと、表示位置と表示サイズを設定するノードをつなげます。
仕上げに、テキストを渡す部分。
とりあえずスペースキーを押すと表示されるようにしました。
完成です。
GIFにしてみました。コマ数が少ないので滑らかじゃないけど。
これを商品レベルにするには、まだまだ必要な仕様やエラー対策なんかがありますが、ちょっとしたプロトタイプなら十分かなと思うのですがいかがでしょう。
ではでは
UI開発者の紳士淑女のみなさま
ステキなスクロールテキストライフを!
ついでに UIネタも募集中!
Widgetを3Dで扱うときに中の関数を呼び出す方法
いつのまにやらセミの声が凄まじい今日この頃、ふと前回の記事更新から随分空いていたことに気付きました。ようやくエディタのバージョン4.16をインストールしました。Widgetに新しいイベントが追加されてたんですよね。Widgetを3Dで扱うときに、EventConstruct の動作するタイミングが悩ましかったのですが、改善されていたらいいなと思いちょっとだけいじってみました。簡単な実験をしてみた結果、特に変わっていない印象です。また後日判ったことがあれば記事にしていきたいと思います。
さてさて今回はWidgetをWorld空間に3Dで表示する際に、中の関数やら変数にアクセスする方法をメモっておこうかと思います。
Widgetアセットをプレイヤーや、背景と同じWorld空間を置く場合、2種類の置き方があります。Actorなどのブループリントに、Widgetコンポーネントとして持たせてしまう方法と、
Add Widget Component ノードを使う方法です。
どちらにせよ、どちらもWidgetコンポーネントという形になります。また、大抵ブループリントActorでくるむことになります。
ここでいう「くるむ」というのは、あくまでも本体はWidgetであって、Actorブループリントは3Dで置くための入れ物というイメージで使っている言葉です。
ただ置くだけならいいのですが、Widgetを置くからには大抵がUI表示のはず。そうなるとゲージやらテキストやらということになるので、内容のセットアップや更新に関数を使うことになるかと思います。その際にこのWidgetコンポーネントがちょっと面倒なのです。
Widgetを3Dにして扱うときは、このWidgetコンポーネントの状態になります。この状態だと中の関数や変数にサクッとアクセスできません。アクセスするにはキャストが必要になります。
このとき必ず必要なノードがあります。 Get User Widget Object ノードです。
一旦 UserWidget型 にした後、改めて本当の姿であるWidgetにキャストします。
下の図は、MyWidget3Dという名前のWidgetを用意した場合の例です。
まずは、Widgetがブループリント内のコンポーネントにしてある場合。
つぎは、Add Widget Componentノードから追加した場合。
キャストが成功したら、キャスト後の値を Promote to Variable(出力ピンの上で右クリック)で変数化しておくと便利です。以後その変数からドラッグすれば、中の関数や変数にアクセスできるようになるからです。
例)
Widgetブループリントで作ったアセットを 3DのWorld空間に置こうとすると、WidgetComponentの状態で置かれる。
そのWidgetの中にアクセスするときは、いったんUserWidget型にしたあと、それぞれのWidget型にキャストしてやる必要があるということになります。
ちょっと面倒ですが、この方法を使えば3DのUIも怖くない。と思うのですがいかがでしょう。
ではでは今回はこの辺で。
ステキな立体UIライフを!
キャラセレを作ってみる おまけ編
続きです。
シンプルな作りですが、キャラセレ画面としてある程度必要と思われる内容まで用意できました。今回はキャラを選ぶにあたっての能力値をグラフで表現したものを作って追加します。
まずはテクスチャ。
40x40 のグレースケールでアルファチャンネルは無しです。
今回のゲージはマテリアルを使わずに増減させます。が、テクスチャ容量を節約したいので、ちょっぴりマテリアルの助けを借ります。
テクスチャのフォーマットをグレースケールにしてしまうとテクスチャのメモリ消費量は 1/4 に減らせられるのですが、UMGで扱えません。そこで下図のようなマテリアルを用意します。
グレースケールなので、Rチャンネル(G、Bのピンでも結果は同じ)のピンをOpacityにつなぐことで、テクスチャをアルファチャンネルとして使えます。
TextureSampleノードの設定が変わります。
(テクスチャ設定の sRGB のチェックを外すとLinearGrayscale になります)
マテリアルができたら、次はグラフ用のWidgetブループリントを新しく作ります。
作ったマテリアルを張り付けるImageパーツをキャンバスに配置します。
このImageパーツにマテリアルをセットしたらヨコに伸ばして使うので設定をいじります。
ひとまず横の長さは400にします。
このパーツは下敷きにするので、適当に暗めのカラーにします。
このImageパーツを複製します。
複製したらカラーを明るくして上に重なるように移動して、さらにZOrderの数値を調整します。
ゲージ部分のパーツが完成です。次にテキストブロックを左横に配置します。
このテキストブロックはブループリントから内容を書き換えるので、 Is Variable にチェックを付けておきます。
さて、肝心のゲージが増減するしくみですが、アニメーションを使います。
新しくアニメーションを作成して、ゲージのサイズをアニメーションさせます。
キーフレームは1秒刻みで、0.0~9.0 にキーを打ちます。動かすのは Slot のSize。
→
1秒あたり 40ずつ サイズを大きくしていきます。
キーを打ち終えたら、
トラックのキーを全てConstantに変えます。マウスドラッグでアニメーションのキーを一気に選択したら、右クリックするとサブメニューが出てくるのでConstantを選択します。Constantはキーとキーの間を補間しません。
Sizeの値をキーセーブしてるのに、トラックには Offset と書かれるのはちょっと混乱しそうです。
アニメーションができたら、あとは、Widgetブループリントでコントロールします。
必要な関数は2つです。
名前をセットする関数
引数でテキストを受け取ってキャンバスに置いたテキストブロックにセットしてやります。
グラフの長さをセットする関数
引数でグラフの長さに対する値を受け取ってアニメーションのキーフレームに変換します。アニメーションの再生は Play Animation To を使います。
このノードは再生する場所を指定できます。↑の Volume はUMGで作ったアニメーションコンポーネントの名前です。
StartとEndの値が同じ場合、アニメーションは動き続けるので、再生したらすぐに止めます。
この、指定した時間で止めてグラフの長さを調整する方法は Flash でアニメーションを制作したことがある方は、すでにお馴染みの方法だろうと思いますがいかがでしょうか?
2つの関数ができたところで、このゲージアセットは完成です。
実装してみる
セレクト画面のメインWidgeを開いて、できたゲージのWidgetをキャンバスに並べます。今回は4つ。ちょっと大きめの枠線は親のキャンバスパネルです。
キャンバスパネルに子供として並べておくと、まとめて移動したり、表示のON・OFFが簡単にできるのでオススメです。キャンバスは親子構造にできるので便利ですが階層が深過ぎると処理負荷があがるのでほどほどに。
キャンバスに並べたら、扱いやすくするために配列にしてしまいます。
Initialize関数を編集します。
先に配列変数を用意しても問題ないですが、オススメの方法をご紹介します。
まずVariablesのリストに並んでいるWidgetを4つドロップします。→①
つぎに一番上のノードのピンからドラッグして Make Array ノードを取り出します。→ ②
Add pin でピンを増やし、ノードを4つ全部つないだら、Make ArrayノードのArrayピンの上で右クリックして Promote to Variable を選択します。 → ③
ゲージWidgetの型の配列変数ができあがります。
次にテキスト型の配列変数を用意します。
これは変数の型が Text なので、探すのが簡単です。配列変数にしたら、Setでグラフにドロップして、MakeArrayノードをつなぎます。テキストはゲージの横に表示する名前になります。
さっそくゲージに名前をセットしてしまいましょう。続きにForEachLoop ノードをつなぎます。(キーボードの[F] を押しながらクリック)
下のようにつなぎます。
この状態で再生してみると、このようになります。
データテーブルを用意する
次に、キャラクターの能力値をまとめたデータテーブルを作ります。
まず先に用意するのはStructure。
コンテンツブラウザで、右クリック > Blueprints > Structure
開いて編集します。中の New Variable ボタンを押すと変数が追加できるので、Integer型の変数を4つ追加します。
Default Value(初期値)は0で構いません。
できたら保存して閉じます。
次に DataTable を用意します。これもコンテンツブラウザで
右クリック > Miscellaneous > Data Table
を選択。
Structureを選べと訊いてくるので、作ったStructureを選んでOKボタンを押します。
ダブルクリックして編集します。下のようなエディタが開くので下段のRowEditorタブにある + ボタンをクリックします → ①
クリックしたらRow Name を変更します。 → ②
この名前はキャラクターのIndex番号になります。後でブループリントから扱うときに数字を使うので 0~7 の半角の数字にしておきます。
キャラクター全員のパラメータを入力できたら保存して閉じます。
再びメインのキャラセレWidgetに戻ります。
関数を一つ新しく追加します。
作ったデータテーブルを読み込んで必要なデータを取り出すために、
Get Data Table Row ノードを使います。
Data Tableの青いピンのところに用意しておいたData Table をセットします。
Row Name No薄い紫のピンには、Integer型の引数からString型を経由してつなぎます。
Integer型 → String型 → Name型
こういった型を変換することをキャストすると言います。私たち人間は「 1 」という文字を見た時、「字」なのか「数」なのかを周辺の情報をもとに判断します。コンピュータはある意味単純なので型を指定してやらないと意図通りに処理してくれません。キャストにはルールがあってキャストできないものがあります。Int型(Integerは省略すると Int)は いきなりName型にできないので、いったんString型にします。
これで、RowNameに該当するデータの有無で右の処理が分岐します。青いピンからは見つかった場合にデータが取り出せます。
データを取り出したらBreake ノードで分解することができます。
Initialize関数で準備したゲージの配列変数をここで使います。Getノードで順番に取り出しながら、ゲージのWidgetに用意しておいた関数に値を渡してやります。
Return Node で戻り値(Success=Boolean型)を設定しているのはデータが取り出せなかったときのエラー対策をとるための準備なので、べつに無くても問題ありません。
関数は出来上がりです。
これをEventGraphにつなぎます。場所は2か所。
最初のカーソル位置とサムネイルにフォーカスをセットする部分と、キー入力を受け付けてフォーカスを切り替える部分です。
出来上がりです。
いかがでしたか?
結構なボリュームになりましたが、だいたいの構造と必要な処理が理解できれば、絵素材の制作を除くと1時間もあれば作れるようになります。今回のオマケ部分はいろんな作り方があるので時間の掛かり方はアイデア次第になりますが、ぜひイロイロ凝ったものを試してみたいですね。
今回の作ってみるシリーズはあまり集中して書く時間が取れなかったので、間があいてしまったのが残念です。前半に比べ後半は細かい説明は省略してますので、記事の内容で分からないとこや間違いの指摘等あれば、お気軽にコメントください。
ではでは
ステキなキャラセレライフを!
追記:
今回のエントリー扱っている絵素材については、いらすとやさんの素材を使わせてもらいました。使用するにあたって事前にやり取りはしておりませんので問題あれば記事を削除いたします。
キャラセレを作ってみる デカキャラ編
サムネイルが登場して、カーソルを動かしてフォーカスを切り替えるとこまでできました。つぎはカーソルの指しているキャラの大きな絵が シャッ!とかシュッ!とか言いながら入ってくるのを作ります。いや言いませんね。オノマトペです。
ではさっそく、新しくWidgetを追加します。
UMGでアニメーション
まずはキャンバスにImageを配置します。
サイズをテクスチャに合わせて調整したら、ポジションをキャンバスの左上にピッタリとつけます。
配置できたら登場のアニメーションを用意します。
今回画面の外からフレームインするように動かします。
UMGでは2つの移動手段があります。一つはSlotの Position。
もう一つはRender Transformの Translation。
移動のアニメーションはPositionで動かすのではなく、Translation をオススメします。なぜなら相対座標でアニメーションできるからです。どの場所に置かれようとも、置かれた位置からの相対位置、どれだけ離れているかをアニメーションキーとするので、あとからPositionを動かして調整するのことができてるのが便利です。一方Positionは絶対座標なので、キーを打ってしまうと位置調整はできません。必ずアニメーションキーを打って覚えさせたポジションに移動してしまうからです。位置を調整しようと思ったら、アニメーションキーをいくつも調整して更新しなければいけないので面倒です。
左からフレームインさせるので、Translationを使ってX座標をマイナスの値からスタートして 0で止めるようにアニメーションキーを打ちます。
移動距離が大きいと短い時間で動かした場合、滑らかな動きは期待できませんが、透明度のアニメーションを加えることで、移動距離を短くできます。この辺のバランスはいろいろ試してみると面白いです。
キャラクターの絵が画面に入ってきたら、キャラクターの名前も出してやります。
キャラクターの名前は1枚のテクスチャにまとめています。
これをサムネイルと同様に、マテリアルで切り出します。
名前は一つあたりのサイズが 1024x128ピクセルなので、
TexCoordinateノードのタイリング設定は、切り出すサイズをテクスチャサイズで割った値です。
1024 ÷ 1024 で 1.0 、128 ÷ 1024 で 0.125
切り出したら、Vの値に足し算すると下に移動するので、Add ノードとAppendVectorノードを使います。 Index と書かれたノードは ScalaeParameterValueノードです。これはブループリントからマテリアルに値を渡すための仕組みです。
横には移動しないので、Constantノードに0を入れてつないでいます。
Apply してエラーが出ないのを確認したら保存して閉じます。
マテリアルができたので、さっきのWidgetに戻ります。
キャンバスに 1024x128の Image を置いたら、作ったマテリアルをセットします。
この名前パーツもアニメーションを付けます。一緒に現れるのでキャラの絵と同じアニメーションに追加します。
これで役者の準備が整いました。
Widgeブループリント
このデカキャラ用のWidgetは、親のWidgetから指定されたキャラクターのIndex番号をもとに絵を切り替えて画面に登場するという役割になります。ユーザーが自由にカーソルを動かしてフォーカスを切り替えるたびに、キャラクターを切り替えます。そのための基礎になる関数を用意します。
引数(Inputパラメータ)はInt型でキャラクターのIndex番号を受け取ります。受け取った値に 0.125 を掛けてキャラ名用のImageパーツにセットしたマテリアルに渡します。
キャラ名はV方向(タテ)に 0.125ずつ並んでいます。
関数の最後でデカキャラのImageパーツの位置をリセットしています。
2つ目の引数はTexture2D型です。これもデカキャラ用のImageパーツに渡します。
直接テクスチャをセットするノードが用意されています。Set Brush from Textureノードです。
この関数はこのWidgetの中から利用する想定です。次は外(親)から呼ばれるイベントを用意します。EventGraphに戻ってカスタムイベントを用意します。このイベントにも引数(パラメータ)を2つ同じように設定します。
このイベントが呼ばれるということは、ユーザーがキー操作してカーソルのフォーカスを切り替えたタイミングです。ユーザーが素早くキー操作しても大丈夫なように、アニメーションの途中かどうかを調べて(Is Animation Plying)、まだ登場の途中であれば即座にアニメーションを止めます。で先に用意しておいた関数をここでつないで、引数を渡してやります。
Delayノードは関数の中の処理が終わり切らないうちに次の処理へ流れないようにするための時間稼ぎです。0を入れると1フレーム(60fpsだったら約16.67ms)だけ進みます(ちょっと自信ない)。ちなみにDelayノードは関数の中では使えません。
最後に登場アニメーションを再生すれば完成。
呼び出してみる
背景やサムネイルを配置しているメインのWidgetブループリントを開いて、デカキャラのWidgetを配置します。
Is Variable にチェックが付いているか確認して、付いていなかったら付けます。
配置したら、Initialize 関数を編集します。
デカキャラのテクスチャを配列にセットします。Texture2D型の配列変数を作って、Setでドロップしたら、MakeArrayノードをつないで、デカキャラのテクスチャを順番通りにセットしてゆきます。
テクスチャの準備ができたら、新たに関数を用意します。
上のWD_Character はキャンバスに配置したデカキャラ用Widgetです。IsVariableにチェックをつけるとグラフにノードとして置くことができます。そこからドラッグして用意しておいたカスタムイベントノードを取り出してつなぎます。
あとは、引数から配列の中身を取り出して渡してやります。
EventGraphでこの関数をつなげば出来上がり。つなぐ場所は2か所。
さっそく再生して確認してみます。
動画を載せたかったのですが、使ってるノートPCでキャプチャがうまくできないのとこのブログではGIFアニメまでなので諦めました。
これでキャラセレのだいたいの要素は揃いました。
次回はオマケのグラフ表示を作って終わりにしたいなと思います。
ではでは
ステキなデカキャラライフを!