みつまめ杏仁

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

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

※ この記事は 内容に誤りがあったため 内容を一部加筆修正しています。 Ver 4.17で検証・作成しています(2019/4/25)

 

今回は、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

 

これを ▼ に適用します。エディタのDtailes(詳細)パネルにあるMaterials です。

f:id:hiyokosabrey:20171006193835p:plain

続けてコリジョンの設定を変更します。

f:id:hiyokosabrey:20190425235227p:plain

Generate Overlap Events のチェックを外すだけでもいいのですが、このマーカー自体コリジョンは不要なので、Collision Presets を No Collision にしておきます。

 

 

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

f:id:hiyokosabrey:20171006194245p:plain

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

f:id:hiyokosabrey:20171006194629p:plain

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

 

Widgetにもコリジョンの設定があるので、▼同様に外しておきます。

f:id:hiyokosabrey:20190425235227p:plain

 

 

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

 

まず変数を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

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

 

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

 

まずは、On Component Begin Overlap イベントから。

f:id:hiyokosabrey:20190425234628p:plain

 


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

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

この変数化したものは用が済んだら消す処理で使います。

 

で、さっそく On Component End Overlap イベントで使います。

f:id:hiyokosabrey:20190425231923p:plain

コリジョンから出たらこのイベントが発動して、スポーンされていたマーカーは消えます。

スポーンしてなくても、このイベントは発動するので、Destroyする対象が無いということで、Warning と Error が Outputログに吐き出されてしまいます。中身が有効かどうかを、 Is Valid ノードで調べています。

  

これで、準備完了です。

 

 

ワールドに置いていく

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

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時間もあれば作れるようになります。今回のオマケ部分はいろんな作り方があるので時間の掛かり方はアイデア次第になりますが、ぜひイロイロ凝ったものを試してみたいですね。

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

 

ではでは

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

 

 

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

 

 

 

 

キャラセレを作ってみる デカキャラ編

サムネイルが登場して、カーソルを動かしてフォーカスを切り替えるとこまでできました。つぎはカーソルの指しているキャラの大きな絵が シャッ!とかシュッ!とか言いながら入ってくるのを作ります。いや言いませんね。オノマトペです。

 

ではさっそく、新しくWidgetを追加します。

 

UMGでアニメーション

まずはキャンバスにImageを配置します。

サイズをテクスチャに合わせて調整したら、ポジションをキャンバスの左上にピッタリとつけます。

f:id:hiyokosabrey:20170625005715j:plain

配置できたら登場のアニメーションを用意します。

今回画面の外からフレームインするように動かします。

UMGでは2つの移動手段があります。一つはSlotの Position

f:id:hiyokosabrey:20170625010626p:plain

もう一つはRender Transformの Translation

f:id:hiyokosabrey:20170625010640p:plain

 

移動のアニメーションはPositionで動かすのではなく、Translation をオススメします。なぜなら相対座標でアニメーションできるからです。どの場所に置かれようとも、置かれた位置からの相対位置、どれだけ離れているかをアニメーションキーとするので、あとからPositionを動かして調整するのことができてるのが便利です。一方Positionは絶対座標なので、キーを打ってしまうと位置調整はできません。必ずアニメーションキーを打って覚えさせたポジションに移動してしまうからです。位置を調整しようと思ったら、アニメーションキーをいくつも調整して更新しなければいけないので面倒です。

 

左からフレームインさせるので、Translationを使ってX座標をマイナスの値からスタートして 0で止めるようにアニメーションキーを打ちます。

移動距離が大きいと短い時間で動かした場合、滑らかな動きは期待できませんが、透明度のアニメーションを加えることで、移動距離を短くできます。この辺のバランスはいろいろ試してみると面白いです。

キャラクターの絵が画面に入ってきたら、キャラクターの名前も出してやります。

 

キャラクターの名前は1枚のテクスチャにまとめています。

f:id:hiyokosabrey:20170625013255p:plain

これをサムネイルと同様に、マテリアルで切り出します。

f:id:hiyokosabrey:20170625013503p:plain

 名前は一つあたりのサイズが 1024x128ピクセルなので、

f:id:hiyokosabrey:20170625221451p:plain

TexCoordinateノードのタイリング設定は、切り出すサイズをテクスチャサイズで割った値です。

1024 ÷ 1024 で 1.0 、128 ÷ 1024 で 0.125

f:id:hiyokosabrey:20170625221815p:plain

切り出したら、Vの値に足し算すると下に移動するので、Add ノードとAppendVectorノードを使います。 Index と書かれたノードは ScalaeParameterValueノードです。これはブループリントからマテリアルに値を渡すための仕組みです。

横には移動しないので、Constantノードに0を入れてつないでいます。

Apply してエラーが出ないのを確認したら保存して閉じます。

 

マテリアルができたので、さっきのWidgetに戻ります。

キャンバスに 1024x128の Image を置いたら、作ったマテリアルをセットします。

f:id:hiyokosabrey:20170625222653p:plain

この名前パーツもアニメーションを付けます。一緒に現れるのでキャラの絵と同じアニメーションに追加します。

f:id:hiyokosabrey:20170625223224p:plain

これで役者の準備が整いました。

 

Widgeブループリント

このデカキャラ用のWidgetは、親のWidgetから指定されたキャラクターのIndex番号をもとに絵を切り替えて画面に登場するという役割になります。ユーザーが自由にカーソルを動かしてフォーカスを切り替えるたびに、キャラクターを切り替えます。そのための基礎になる関数を用意します。

f:id:hiyokosabrey:20170625225919p:plain

引数(Inputパラメータ)はInt型でキャラクターのIndex番号を受け取ります。受け取った値に 0.125 を掛けてキャラ名用のImageパーツにセットしたマテリアルに渡します。

キャラ名はV方向(タテ)に 0.125ずつ並んでいます。

関数の最後でデカキャラのImageパーツの位置をリセットしています。

2つ目の引数はTexture2D型です。これもデカキャラ用のImageパーツに渡します。

直接テクスチャをセットするノードが用意されています。Set Brush from Textureノードです。

 

この関数はこのWidgetの中から利用する想定です。次は外(親)から呼ばれるイベントを用意します。EventGraphに戻ってカスタムイベントを用意します。このイベントにも引数(パラメータ)を2つ同じように設定します。

f:id:hiyokosabrey:20170625230302p:plain

このイベントが呼ばれるということは、ユーザーがキー操作してカーソルのフォーカスを切り替えたタイミングです。ユーザーが素早くキー操作しても大丈夫なように、アニメーションの途中かどうかを調べて(Is Animation Plying)、まだ登場の途中であれば即座にアニメーションを止めます。で先に用意しておいた関数をここでつないで、引数を渡してやります。

Delayノードは関数の中の処理が終わり切らないうちに次の処理へ流れないようにするための時間稼ぎです。0を入れると1フレーム(60fpsだったら約16.67ms)だけ進みます(ちょっと自信ない)。ちなみにDelayノードは関数の中では使えません。

最後に登場アニメーションを再生すれば完成。

 

 

呼び出してみる

背景やサムネイルを配置しているメインのWidgetブループリントを開いて、デカキャラのWidgetを配置します。

f:id:hiyokosabrey:20170625232545j:plain

Is Variable にチェックが付いているか確認して、付いていなかったら付けます。

配置したら、Initialize 関数を編集します。

f:id:hiyokosabrey:20170625232948p:plain

デカキャラのテクスチャを配列にセットします。Texture2D型の配列変数を作って、Setでドロップしたら、MakeArrayノードをつないで、デカキャラのテクスチャを順番通りにセットしてゆきます。

 

テクスチャの準備ができたら、新たに関数を用意します。

f:id:hiyokosabrey:20170625233540p:plain

上のWD_Character はキャンバスに配置したデカキャラ用Widgetです。IsVariableにチェックをつけるとグラフにノードとして置くことができます。そこからドラッグして用意しておいたカスタムイベントノードを取り出してつなぎます。

あとは、引数から配列の中身を取り出して渡してやります。

 

EventGraphでこの関数をつなげば出来上がり。つなぐ場所は2か所。

f:id:hiyokosabrey:20170625234214p:plain

f:id:hiyokosabrey:20170625234223p:plain

さっそく再生して確認してみます。

 

f:id:hiyokosabrey:20170625234434j:plain

動画を載せたかったのですが、使ってるノートPCでキャプチャがうまくできないのとこのブログではGIFアニメまでなので諦めました。

 

これでキャラセレのだいたいの要素は揃いました。

次回はオマケのグラフ表示を作って終わりにしたいなと思います。

ではでは

ステキなデカキャラライフを!

 

 

 

 

キャラセレを作ってみる カーソルを動かす

最近話題のハンドスピナーが売っていたので買ってみたのですが、振るとチリチリと何やら小さなパーツが跳ねる音がします。回すとシャーと音がして1分も持ちませんが雰囲気を楽しむ程度には堪能できた、気がします。

 

さてさて前回の続きです。まだまだ続いてます。

サムネイルが登場したところで、次はカーソルです。

マテリアルで回す

カーソルは動きがある方が分かりやすいのでアニメーションをつけます。くるくると回るだけです。今回はただ単純に回るだけなので、マテリアルで回すことにします。

 

カーソル用のマテリアルを用意します。作り方の基本はサムネイルのときと同じですがシンプルです。テクスチャを回すのは CustomRotator を使います。

f:id:hiyokosabrey:20170612235634p:plain

マテリアルができたら、メインのWidgetにカーソルを追加します。

 

メインのWidgetを開いたら、キャンバスに新しい Imageをドロップします。

f:id:hiyokosabrey:20170613000218j:plain

サイズはテクスチャのサイズ。サムネイルの上に乗るので、ZOrderの値はWrapBoxよりも大きい値にします。このImageパーツに作ったカーソルのマテリアルをセットします。Appearance > Brush > Image

f:id:hiyokosabrey:20170613001358j:plain

セットするといきなり回っている状態で挿し変わります。こういった単純なループアニメーションは、UMGのタイムラインよりもマテリアルで作った方がシンプルな作りになるのでオススメです。調整もマテリアルをいじるだけで済みます。

ポジションはサムネイルが見えていないので調整しにくいですが、とりあえず今回はWrapBoxと同じ座標に置いておきます。

 

カーソルが画面に存在するということは、ユーザーに「ぜひ操作してください」というアピールをしていることになります。登場演出がすべて終わって、操作を受け付けるよ、という状態になって初めてカーソルは登場するのが基本です。画面に最初から居るといろいろ不都合があるので、非表示にしておきます。Visibility をHiddenにします。

f:id:hiyokosabrey:20170613002355p:plain

 

 

カーソルを表示する

パーツの次はブループリントを準備していきます。

前回のサムネイルを並べる関数にあとひとつ要素を追加します。

f:id:hiyokosabrey:20170613003359p:plain

この変数は、サムネイルをCreate Widgetする度に一定の値が加算されていました。なのでこのタイミングでは最初のサムネイルが登場してから最後のサムネイルが登場するまでの時間が数値として入っています。これを戻り値(ReturnValue)として外に渡してやるのです。

ここでいったんコンパイルしましょう。後から関数を編集した場合で、引数や戻り値が変更になると、関数の外でもピンの状態が変わってしまうため、接続が切れたりします。そして大抵エラーが出ます。

その場合は慌てずに、エラー表示のついている関数ノードの上で右クリックして、Refresh Node を選んだ後でもう一度コンパイルすると大抵エラーが解消します。

 

EventGraphに戻って関数ノードを見てみると、右側にピンが追加されているはずなので、この値を Set Timer by Event ノードに渡します。

f:id:hiyokosabrey:20170615003235p:plain

そしてカスタムイベントを追加してこのSet Timer by Eventとつなぎます。

 f:id:hiyokosabrey:20170615003400p:plain

8個あるサムネイルの8個目の登場時間が戻り値ですので、タイマーを使ってすべてのサムネイルの登場を待ちます。ただ登場のアニメーションが終わったわけではないので、Delay ノードで登場のアニメーションの分だけ待ちます。

f:id:hiyokosabrey:20170615003524p:plain

充分待ったらようやくカーソルの表示を許可してやりたいのですが、もう少し準備が必要です。カーソルの居場所と選択中の場所を管理する変数を用意します。

居場所は Index番号で管理するのでInteger型にします。FocusIndex という名前にしてSetでつなぎます。値は最初にカーソルがあたっている場所になります。

f:id:hiyokosabrey:20170615004056p:plain

 

ここで関数を用意します。この関数には Index番号からカーソルの座標を計算するという仕事をさせます。

f:id:hiyokosabrey:20170615004904p:plain

 横に4つ並ぶので、演算記号 %(剰余)を使って X座標を。縦方向は4つ毎に1段下に進むので単純に4で割り算してY座標となります。Int型同士で割り算をすると答えは整数のままで小数にはなりません。

 この % と ÷ を使った座標計算については過去記事を貼っておきます。

Widgetでタイル型のボタンをグリッド状に並べる

 

f:id:hiyokosabrey:20170617234201p:plain

XとYに サムネイルの表示間隔を掛け算してやると、サムネイル毎のカーソル位置がバッチリ出てきます。

右の方の青いのは、Vector2D型というタイプの値を扱っています。これは2Dという名前でなんとなく想像がつくかと思いますが、XとYやUとVのような関連する2つのFloat型が1つにまとめられているというものです。単に2つのFloat型の変数がまとめれれているだけなので、まったく関連の無くても問題は無いです。

f:id:hiyokosabrey:20170618000350p:plain

CursorOffsetPosという変数(ローカル変数ではない)を追加しています。これは

サムネイルたちのポジションは画面の右下にあります。画面は左上が0原点なので、いくらかずらしてやらないといけません。そのズレを加算して補正するための変数です。

f:id:hiyokosabrey:20170618002309j:plain

この変数はこの関数内でGetでつないでいるので、当然どこかで値を入れておく必要があります。変数を作ったときに初期値入れておくことができますが、せっかくなんでInitialize関数でSetしておきます。

サムネイルたちはWrapBoxの中に並びます。WrapBoxが親でサムネイルは子供なのでWrapBoxのポジションを起点にするのが良さそうです。下図のようにつなぐと、後からWrapBoxの位置を変えてもちゃんとついてきてくれます。

f:id:hiyokosabrey:20170618004231p:plain

 

編集タブを座標計算用の関数に戻します。

今回この関数はピュア型にしてみようと思います。Pure=純粋ということでプログラマ的に純粋関数とか言われたりもするようです。方法は、グラフの紫のノードをクリックすると左のDetailsタブの中身が下図のように変わるので、Pure のチェックボックスにチェックを付けるだけです。

f:id:hiyokosabrey:20170617235453p:plain

これでこの関数は完成です。早速この関数をEventGraphの方に並べてみます。

ドロップするといつもと様子が違います。白いピンがありません。

f:id:hiyokosabrey:20170617235802p:plain

ここで、計算した座標を受け取る変数 CursorDestinationPos を追加してつなぐと下のようになります。この変数は当然Vector2D型です。

f:id:hiyokosabrey:20170618004400p:plain

Destinationは目的地という意味で名付けました。

カーソルの座標は計算することができたので、次にその座標をカーソルに反映させないとカーソルの位置は変わりません。そこでカーソルを指定の位置に表示する関数を用意します。中身はいたってシンプル。SetCursorPos関数。実際にはスペースは入れていませんが、エンジンが勝手に、単語の間にスペースを入れてくれます。

f:id:hiyokosabrey:20170618131829p:plain

これもつないでいきます。ポジションも決まったので、表示しちゃいましょう。

f:id:hiyokosabrey:20170618132524p:plain

ここでいったん画面を確認してみましょう。

f:id:hiyokosabrey:20170618132748j:plain

出現の演出が終わってからカーソルが表示されて入ればOK。

上の画像はFocusIndexの値に5を入れています。

カーソルが無事に指定した場所に出るようになったので、次にサムネイルのフォーカスを切り替える関数を用意します。

f:id:hiyokosabrey:20170618223940p:plain

 Integer型の引数を一つ受け取って、サムネイルの配列からGETノードで取り出して、サムネイルのWidgetに用意しておいたフォーカス状態にする関数をつなぎます。

これをEventGraphの方につなぎます。引数にはFocusIndex変数をつないでやります。

f:id:hiyokosabrey:20170618224244p:plain

 これでようやく指定した場所にカーソルが表示されて、さらにその指定のサムネイルがフォーカス状態になるというところまで来ました。

 

いよいよキー入力に合わせて移動させます。

 

 

カーソルを動かす

ここまでは、作り手が決めた通りに表示の段取りが進んできましたが、カーソルを動かすとなると、ユーザーの好き勝手なキー入力に応えるしくみが必要です。そのためには常に待機していてユーザーのキー入力を見張っておくことになります。それを実現するのがEventTickノードです。

f:id:hiyokosabrey:20170618225527p:plain

Widgetブループリントに最初から置かれているノードで、ここにつないだノードは再生すると、再生を止めるまでずーっと処理され続けます。なので、ここでユーザーのキー入力を見張ることにします。見張ると言っても、最初からではなくサムネイルの入場演出が終わってカーソルが出現してからにしたいので、フラグを使います。

新しくBoolean(ブーリアン)型の変数を一つ追加して、さっきのカーソルのフォーカスをセットする関数の次につなぎます。isActive という名前にしました。チェックを付けてます。

f:id:hiyokosabrey:20170618230518p:plain

ここチェックを付けるということは、それ以前はチェックが付いていないということなので、初期値は付けないでおきます。

f:id:hiyokosabrey:20170618230952p:plain

Boolean型の変数は True(真) と False(偽)の2つの状態を扱います。スイッチみたいなものです。

チェックが付いている  → True(=スイッチON、または「フラグが立った」)

チェックが外れている → False(=スイッチOFF、または「フラグが寝た」)

 

再生開始時は外れていて、カーソルが出現したらチェックが付くことになりました。

この変数の変化をさきほどのEventTickで見張るのです。

f:id:hiyokosabrey:20170618231655p:plain

Banch(ブランチ)ノードをつないでそのCondtionのピンに変数をつなぎます。ブランチは枝(→分岐)という意味で、True ときとFalse の時で流れを変えることができます。今回はTrueに続きのノードをつないで、Falseには何もつながないでおけばいいのです。

 さて、このTrueのときにつなぐのが、キーを押したか押してないかをチェックする処理です。以前の記事で紹介しているので、今回は説明を省きます。

 limesode.hatenablog.com

 このマクロライブラリを用意しておくと便利ですよ~ということで、始めたキャラセレを作ってみるという企画だったのを改めて再認識したところで次に進みましょう。

このマクロをTrueのピンにつなぎます。

f:id:hiyokosabrey:20170618233216p:plain

GetPlayerControllerノードが必要なので引数としてつなぎます。

 ユーザーがキーを押した瞬間に押したキーのところにつないだノードが処理されます。カーソルの位置とフォーカスの位置はFocusIndexという変数で指定できます。つまりこのFocusIndexという変数の値をどうにかすればよいのです。ここで改めてサムネイルの並びを確認してみます。

f:id:hiyokosabrey:20170619222944p:plain

カーソルを右に動かす場合、Indexの値は +1すれば良さそうです。逆に左に動かす場合は -1 すれば良さそうです。問題になるのはそれぞれ端っこにいる場合です。たとえば右に動かす場合、3の位置にいるとき、+1 するとカーソルは 左下の位置に移動して、7の位置にいるときは 8 になってサムネイルの数をオーバーしてしまいます。逆も同じです。

この辺りの問題を念頭に入れつつ、Indexの値を増やしたり減らしたりする関数を用意します。

まずは単純に右に移動する関数から。

その前に、サムネイルのボタンをアンフォーカスする関数を用意しておきます。

f:id:hiyokosabrey:20170619224025p:plain

先に用意した関数と同じ構造です。指定したサムネイルの配列からGETで取り出してアンフォーカスのイベントを呼び出すだけです。

 

で、右に移動したときの関数は以下のようになります。

f:id:hiyokosabrey:20170619225120p:plain

計算で解決する方法もあるのですが、説明がややこしくなりそうなので、ベタな方法ですがSwitchノードを使いました。最初にフォーカスしていたサムネイルを元にアンフォーカスし、その直後に数値を見て分岐する Switch on Int ノードで切り分けています。切り分けた結果は大きく3つに分かれます。素直に+1 するものと 右端で+1下ときのものです。その結果を一旦ローカル変数TempIndexに入れます。Switchノードで道が分かれるからこそできる手法です。どのルートを通ったかで、TempIndexの内容が変わっています。それを再び FousIndex変数の入れて値を書き換えます。新しくなったFocusIndex変数の値でサムネイルボタンをフォーカスして終了です。

 

同じように左に移動したときの関数を用意します。複製するとラクちんです。

f:id:hiyokosabrey:20170619230427p:plain

違いは、Switchノードの先の分かれ道ですが理屈は同じなので、サムネイルのIndex番号がどのように並んでいるかを確認しながらつなげばそんなに難しくはないはずです。

上の足し算ノードですが、ここに -1 が入っていることに注目です。引き算ノードにして1を入れれば結果は同じですが、関数を複製したのであればノードを入れ替えなくていいので楽です。1を引くのも、-1を足すのも同じだからです。

 

ちょっと話はズレますが、普通の変数とローカル変数に違いについて。

FocusIndex変数はどの場所からでも使用できて、しかも中の値は共有します。変な例えかもしれないですが、アチコチにあるATMからお金出し入れする感じに似てると思います。結果的に変動するのは一つしかない口座の残高。それに引き換えローカル変数の場合は、関数毎に小さな口座を開設することになるので同じ名前の口座名だけど関数が違うと中の残高も違うという使い方ができます。

 

この2つの関数をさっそくEventGraphの方につないでみましょう。

f:id:hiyokosabrey:20170619232340p:plain

再生してみると、サムネイルのフォーカスが切り替わっていくのが分かると思います。

端まで行ったら反対側に移動していればSwitchノードが正しく機能している証拠。

 

この調子で今度は上下の分を用意します。今回のキャラセレは2段しかありません。言ってしまうと、上下どちらにいようとも、上下のキーどちらを押そうとも上下の場所を入れかえればいいだけです。

f:id:hiyokosabrey:20170619233133p:plain

それぞれの段で ±4 すればいいだけです。

というわけで、EventGraphでつなぐときはUpとDownをまとめてつなぎます。

f:id:hiyokosabrey:20170619233337p:plain

これでフォーカスの切り替えは完璧です。

 

最後に要のカーソルを移動させましょう。

カーソルを指定の場所に表示する関数は用意してあります。さらに目的地を保持する変数も用意済みです。FocusIndexの値はすでにキー入力に合わせて変動しています。

カーソル移動のための関数を用意します。

f:id:hiyokosabrey:20170619234321p:plain

しくみについては過去記事を貼っておきます。

 

limesode.hatenablog.com

 この関数をEventGraphのマクロにつなぎます。

 f:id:hiyokosabrey:20170619234701p:plain

最期のNo Input は何も入力がなかった場合に流れてくるところで、ユーザーがキーを押さなければ常に実行されます。

FocusIndexが更新されてフォーカス位置が変わっても、CursorDestinationPosの値が更新されていません。目的地の更新がないとカーソルは動かないので、更新するようにします。

f:id:hiyokosabrey:20170619235312p:plain

これでカーソルの動きは完成です。

EventGraphはだいたいこんな感じになりました。

f:id:hiyokosabrey:20170619235644p:plain

再生して触ってみましょう。

f:id:hiyokosabrey:20170619235958j:plain

f:id:hiyokosabrey:20170620000041j:plain

f:id:hiyokosabrey:20170620000051j:plain

フォーカスしたサムネイルを追いかけてカーソルがするすると動きます。

 

 

今回は以上です。

次回は、フォーカスに合わせて大きいイラストを表示するところを作っていきます。

ではではステキなカーソルライフを!

 

キャラセレを作ってみる サムネイルを並べる

どうもです。まだ続いてますよ~。

あまり間が開かないようにしたいのですが、なかなか時間がとれなくてペースが悪いですが頑張って更新していきます。UE4でUIを作ることになった初心者の方向けに、オペレーションを幾分丁寧に書いてるのもありますが、そろそろ扱いに慣れてきている頃合いだと思うのでスピード上げていきたいと思います。

サムネイルを並べる前に、いったん過去記事のリンクを並べておきます。

タイトル名がいつの間にか変わっていたのに気づいて修正しました。

  1. キャラセレを作ってみる 素材編
  2. キャラセレを作ってみる 背景編
  3. キャラセレを作ってみる サムネイル編
  4. キャラセレを作ってみる サムネイル編2

今回サムネイルを画面に並べるところを作ります。

コンテンツブラウザを見ると、WidgetBlueprintアセットが2つあります。2回目の記事で背景を置いたWidgetを編集します。

 

Wrapボックスを使ってみる

編集モードをDesignerに切り替えて、キャンバスにWrap Box(ラップボックス)をドロップします。

Wrap BoxはPaletteタブのPanelカテゴリの中にあります。

f:id:hiyokosabrey:20170610234443p:plain

f:id:hiyokosabrey:20170610234656p:plain

キャンバスの中で大体の位置を決めたら、設定を変更します。

f:id:hiyokosabrey:20170610235219p:plain

 サイズは、256x256のサムネイルが4つ横に並ぶので1024、縦に2つ並ぶので512にしています。

この WrapBoxはブループリントから触るので、isVariableチェックボックスにチェックを付けます。名前は適当で構わないですが、ブループリントで扱う場合パーツの種類で動作や扱えるノードが変わってくるので、なるべくアンダーバーより前は名前を残すことをおすすめします。なので、上の画像だと、「164」 の部分をリネームする感じです。

ちなみに isVariable にチェックを付けるとキャンバスに置いているパーツのリストが変化します。

f:id:hiyokosabrey:20170611000757p:plain

 ブラケット[ ]が外れます。小さな変化ですが、知っておくと後で以外に役立ちますよ。

 

 

これでキャンバスの準備は完了です。

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

 

Initialize関数を作る

イニシャライズと読みます。役割はこれから仕事をするための準備をするところです。機能の多いものを作ろうとすると、何かと下ごしらが必要になります。UIを作っていると後から機能が増えたりすることはよくあることで、準備するものもまた後から増えることになります。なくても何の問題もないですがオススメしたいので作っていきます。

まず関数を新しく追加します。名前は Initialize とか Init でOK。

f:id:hiyokosabrey:20170611002547p:plain

次にサムネイルの並びを設定する配列変数を新しく追加します。

配列変数は VriableType のプルダウンメニュー右側にあるアイコンをクリックして切り替えることで簡単に変更できます。

f:id:hiyokosabrey:20170611003147p:plain

f:id:hiyokosabrey:20170611003852p:plain

 

キャラクターはID番号を振って管理するのが一般的です。

分かりやすく0~7の整数で管理したいので、Integer型で用意します。

できた配列変数をさっきのInitialize関数の中に Set でドロップしてつなぎます。

f:id:hiyokosabrey:20170611004843p:plain

左右に四角いピンがあります。値を入力したいので、左のピンからドラッグして、Make Array ノードを取り出します。

f:id:hiyokosabrey:20170611005145p:plain

取り出せたら、Add pin + をクリックして[7]まで増やします。

f:id:hiyokosabrey:20170611005430p:plain

こうなります↓

f:id:hiyokosabrey:20170611005756p:plain

値が全部 0 になっているので、ここにキャラの番号を入れていきます。

サムネイルのテクスチャは ↓ のように並んでいるので、f:id:hiyokosabrey:20170611004639p:plain

数字が重複しないように入力します。順番はお好みで。

 

0~7を入力し終わったら、関数はいったん編集完了です。

EventGraph に戻ります。

f:id:hiyokosabrey:20170611010533p:plain

Event Construct という赤いイベントノードに、できたてのInitialize関数をつないでやります。

f:id:hiyokosabrey:20170611010723p:plain

よくみたら、関数のノードなのにイベントと同じアイコンになってます。ちょっと難しくなるのですが、関数は仕事をしたら結果や成果を報告することができます。戻り値とか言うとプログラマっぽくなりますね。ノードの右につく出力ピンにあたります。それがあればfのアイコン。なければイベントのアイコンになります。

 

 WrapBoxにサムネイルを追加する

次にいよいよサムネイルを並べる関数を作ります。

f:id:hiyokosabrey:20170611013048p:plain

ここに For Each Loop ノードをつなぎます。(キーボードの F を押しながらクリック)さらに先に作っておいた配列変数をGetで取り出して ForEachLoopノードにつないでやります。

f:id:hiyokosabrey:20170611013429p:plain

この「ふぉーいーち」ノードは配列変数のために用意されているノードで、配列変数に仕込まれた値のぶんだけ回しますよ。という働きをします。今回用意した配列変数は [0]~[7]まで値が入っているので、8回だけ回してくれます。ここでいう「回す」というのは、右上の LoopBody と書かれた実行ピンの先につながっているものが対象です。配列変数の中身の分だけ回すと、右下のComplete のピンから出てきます。

 

次にCreate Widgetノードを取り出して、Loop Body の実行ピンにつなぎます。

f:id:hiyokosabrey:20170611145912p:plain

ノードのClassのところに、サムネイル用に用意したWidgetをセットしてやります。

セットしたら、CreateWidgetノードの右に ReturnValue ピンがあるので、このピンの上で右クリック 、メニューから Promote to Variable を選択します。

f:id:hiyokosabrey:20170611201817p:plain

新しく変数が作られるので、

f:id:hiyokosabrey:20170611201854p:plain

これをいったんグラフから削除(Deleteキー)します。

消しても左のVariableタブの中にはちゃんと残っているので、これを配列型に切り替えます。名前も変えておきます。WD_Buttonsにしました。

改めてこの配列を Get でグラフにドロップします。

f:id:hiyokosabrey:20170611201842p:plain

この配列は空っぽです。そこでForEachLoopで8回CreateWidgetした結果(ReturnValue)を配列に追加するのでAddノードをつなぎます。Addノードは配列専用のノードで配列変数からドラッグして探すとすぐに見つかります。

f:id:hiyokosabrey:20170611202133p:plain

この配列は、後から個別にサムネイルのボタンを制御するために利用します。

 

いよいよ WrapBox に追加します。

isVariable のチェックを付けると、Variableリストの中に、WrapBoxが変数として存在するようになるので、これをグラフに Get でドロップします。

ドロップしたら、そこから Add Child Wrap Box ノードを取り出して下図のようにつなぎます。

f:id:hiyokosabrey:20170611203027p:plain

さらに、サムネイルを作ったときに用意したセットアップ用の関数を取り出してつなぎます。この関数は、サムネイルのWidget内にしか存在しないので、ReturnValueからドラッグした場合だけ検索できます。

f:id:hiyokosabrey:20170611205533p:plain

f:id:hiyokosabrey:20170611205157p:plain

この関数にはパラメータ(引数)を渡せるように作っていました。

f:id:hiyokosabrey:20170611210147p:plain

なので、ピンに値を渡せるようにつなぎます。

f:id:hiyokosabrey:20170611210459p:plain

ForEachLoopから出てくる値は2つあります。

Array Element は、 配列の中身(Initialize関数で Make Array で仕込んだ中身)

Array Index は、順番(0~)

まだパラメータのピンが残ってますが、早く表示してみたいので、次に進みます。

これで、WidgetがCreateされて、作ったそばから配列に積んでいって、WrapBoxに子供として追加して、最後にサムネイルの関数に必要な値を渡す仕事が1セット。合計8セット行われるようにできました。

この仕事が終わったらサムネイルの出現用のアニメーションを呼びたいので、下図のようにつなぎます。

f:id:hiyokosabrey:20170611211727p:plain

Completedのピンから、また次のForEachLoopをつないで、サムネイルの中のReady関数をつないでやります。仕上げにReturnノードをつないでおきます。

だいたいできました。

ここで、EventGraphに戻って、この関数をつなぎます。

f:id:hiyokosabrey:20170611212441p:plain

 

f:id:hiyokosabrey:20170611212452p:plain

 

これでコンパイルして保存します。

 

レベルを用意する

Widgetを表示するためには、レベルを用意する必要があります。レベルはコンテンツブラウザから作成します。

f:id:hiyokosabrey:20170611220802p:plain

適当に名前を付けたら、ダブルクリックします。

レベルを開くとビューポートの右下が変化します。下は”Select”という名前のレベルを作った場合。

f:id:hiyokosabrey:20170611221421p:plain

 Persistant というのは今編集対象のレベルを意味しています。

ここが切り替わったのを確認したら、レベルブループリントを編集します。

レベルブループリントの編集方法は少し変わっていてエディタの Blueprints ボタンの右にある小さな▼をクリックしたらコンテキストメニューが出てくるので、この中からOpen Level Blueprint を選びます。

f:id:hiyokosabrey:20170611221837p:plain

グラフエディタが開いたら、Event BeginPlay ノードに Create Widget ノードと、Add to Viewport ノードをつなぎます。

f:id:hiyokosabrey:20170611222534p:plain

CreateWidgetノードの、Class のところは、今回作ったメインのWidgetをセットします。

コンパイルして保存しておきます。レベルは保存の方法も少し変わっています。ビューポートの上にあるSave Current ボタンから保存します。

f:id:hiyokosabrey:20170611222951p:plain

レベルが用意できたので、さっそくPlayしてみましょう。

保存の時と同じくビューポートの上にあるPlayボタンをクリックします。

うまく表示されるでしょうか?

f:id:hiyokosabrey:20170611223459j:plain

おや? いつまで経ってもサムネイルが表示されません。Escキーを押して再生を止めます。

わざとらしいことしてごめんなさい。原因はサムネイルWidgetに仕込んだ Set Timer by Event ノードです。このノードは数値を受け取ると、受け取った時間だけ待機してその後つながったイベントを実行します。 実は 0 を渡すと仕事をしなくなるのです。

f:id:hiyokosabrey:20170611224330p:plain

気を取り直して、先ほどの関数を編集します。

サムネイルのSetupButton関数に0以外の数値を入れて再生してみましょう。

f:id:hiyokosabrey:20170611224801p:plain

コンパイルしてエラーがないのを確認したら保存してPlay・・・

f:id:hiyokosabrey:20170611225109j:plain

ようやく表示されました。

ただ一斉に出てきて面白くないので一工夫します。このままだと、わざわざDelayTime入れた甲斐もないです。

ふたたびさっきの関数を編集します。

まずローカル変数を用意します。左のMyBlueprintタブの中に Local Variables  というカテゴリがあるので、そこに追加します。

f:id:hiyokosabrey:20170611225641p:plain

DelayTimeは小数点で扱うのでFloat型です。ちなみにTempというのは「テンポラリ」の略で、「一時的な」という意味でよく使われます。このローカル変数はこの関数内だけで活躍します。関数の外には一切出ていけず一生をここで過ごします。さらに関数が仕事を終えるとこのローカル変数はメモリから破棄されて消えてしまいます。

実はそこがメリットだったりします。

で、さっそくつないでいきます。ローカル変数とはいえ扱い方は普通の変数とまったく同じです。グラフに ドロップして使います。

GetとSet の2タイプ をドロップします。

f:id:hiyokosabrey:20170611230928p:plain

間にFloat 同士の足し算 をするノードを挟みます。

f:id:hiyokosabrey:20170611230727p:plain

慣れないと解りにくいかもですが、

まず自分の値をGetして、そこに値を足して、自分自身の値を更新(Set)しています。

この処理を、Add Child Wrap Box の後に 瞬間的に行います。そしてすぐその値を渡しています。

足し算は適当にいろいろな数値で試してみてください。ちなみに 1.0 は1秒と同じ長さです。

これで完成です。

コンパイルして保存したら、再びPlayして確認してみましょう。

 

f:id:hiyokosabrey:20170611232253j:plain

しゅるしゅる~

f:id:hiyokosabrey:20170611232314j:plain

f:id:hiyokosabrey:20170611232326j:plain

やった。うまくいきました。

やっぱりこういうインタラクションがあるのと無いのとでは印象が変わってきます。さらに視線誘導にも役立ちます。

 

今回はここまでにします。

なかなかのボリュームになってしまいました。

サムネイルも表示できたので、次回はカーソルの登場です。

ではではステキなサムネライフを!