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アニメまでなので諦めました。
これでキャラセレのだいたいの要素は揃いました。
次回はオマケのグラフ表示を作って終わりにしたいなと思います。
ではでは
ステキなデカキャラライフを!
キャラセレを作ってみる カーソルを動かす
最近話題のハンドスピナーが売っていたので買ってみたのですが、振るとチリチリと何やら小さなパーツが跳ねる音がします。回すとシャーと音がして1分も持ちませんが雰囲気を楽しむ程度には堪能できた、気がします。
さてさて前回の続きです。まだまだ続いてます。
サムネイルが登場したところで、次はカーソルです。
マテリアルで回す
カーソルは動きがある方が分かりやすいのでアニメーションをつけます。くるくると回るだけです。今回はただ単純に回るだけなので、マテリアルで回すことにします。
カーソル用のマテリアルを用意します。作り方の基本はサムネイルのときと同じですがシンプルです。テクスチャを回すのは CustomRotator を使います。
マテリアルができたら、メインのWidgetにカーソルを追加します。
メインのWidgetを開いたら、キャンバスに新しい Imageをドロップします。
サイズはテクスチャのサイズ。サムネイルの上に乗るので、ZOrderの値はWrapBoxよりも大きい値にします。このImageパーツに作ったカーソルのマテリアルをセットします。Appearance > Brush > Image
セットするといきなり回っている状態で挿し変わります。こういった単純なループアニメーションは、UMGのタイムラインよりもマテリアルで作った方がシンプルな作りになるのでオススメです。調整もマテリアルをいじるだけで済みます。
ポジションはサムネイルが見えていないので調整しにくいですが、とりあえず今回はWrapBoxと同じ座標に置いておきます。
カーソルが画面に存在するということは、ユーザーに「ぜひ操作してください」というアピールをしていることになります。登場演出がすべて終わって、操作を受け付けるよ、という状態になって初めてカーソルは登場するのが基本です。画面に最初から居るといろいろ不都合があるので、非表示にしておきます。Visibility をHiddenにします。
カーソルを表示する
パーツの次はブループリントを準備していきます。
前回のサムネイルを並べる関数にあとひとつ要素を追加します。
この変数は、サムネイルをCreate Widgetする度に一定の値が加算されていました。なのでこのタイミングでは最初のサムネイルが登場してから最後のサムネイルが登場するまでの時間が数値として入っています。これを戻り値(ReturnValue)として外に渡してやるのです。
ここでいったんコンパイルしましょう。後から関数を編集した場合で、引数や戻り値が変更になると、関数の外でもピンの状態が変わってしまうため、接続が切れたりします。そして大抵エラーが出ます。
その場合は慌てずに、エラー表示のついている関数ノードの上で右クリックして、Refresh Node を選んだ後でもう一度コンパイルすると大抵エラーが解消します。
EventGraphに戻って関数ノードを見てみると、右側にピンが追加されているはずなので、この値を Set Timer by Event ノードに渡します。
そしてカスタムイベントを追加してこのSet Timer by Eventとつなぎます。
8個あるサムネイルの8個目の登場時間が戻り値ですので、タイマーを使ってすべてのサムネイルの登場を待ちます。ただ登場のアニメーションが終わったわけではないので、Delay ノードで登場のアニメーションの分だけ待ちます。
充分待ったらようやくカーソルの表示を許可してやりたいのですが、もう少し準備が必要です。カーソルの居場所と選択中の場所を管理する変数を用意します。
居場所は Index番号で管理するのでInteger型にします。FocusIndex という名前にしてSetでつなぎます。値は最初にカーソルがあたっている場所になります。
ここで関数を用意します。この関数には Index番号からカーソルの座標を計算するという仕事をさせます。
横に4つ並ぶので、演算記号 %(剰余)を使って X座標を。縦方向は4つ毎に1段下に進むので単純に4で割り算してY座標となります。Int型同士で割り算をすると答えは整数のままで小数にはなりません。
この % と ÷ を使った座標計算については過去記事を貼っておきます。
XとYに サムネイルの表示間隔を掛け算してやると、サムネイル毎のカーソル位置がバッチリ出てきます。
右の方の青いのは、Vector2D型というタイプの値を扱っています。これは2Dという名前でなんとなく想像がつくかと思いますが、XとYやUとVのような関連する2つのFloat型が1つにまとめられているというものです。単に2つのFloat型の変数がまとめれれているだけなので、まったく関連の無くても問題は無いです。
CursorOffsetPosという変数(ローカル変数ではない)を追加しています。これは
サムネイルたちのポジションは画面の右下にあります。画面は左上が0原点なので、いくらかずらしてやらないといけません。そのズレを加算して補正するための変数です。
この変数はこの関数内でGetでつないでいるので、当然どこかで値を入れておく必要があります。変数を作ったときに初期値入れておくことができますが、せっかくなんでInitialize関数でSetしておきます。
サムネイルたちはWrapBoxの中に並びます。WrapBoxが親でサムネイルは子供なのでWrapBoxのポジションを起点にするのが良さそうです。下図のようにつなぐと、後からWrapBoxの位置を変えてもちゃんとついてきてくれます。
編集タブを座標計算用の関数に戻します。
今回この関数はピュア型にしてみようと思います。Pure=純粋ということでプログラマ的に純粋関数とか言われたりもするようです。方法は、グラフの紫のノードをクリックすると左のDetailsタブの中身が下図のように変わるので、Pure のチェックボックスにチェックを付けるだけです。
これでこの関数は完成です。早速この関数をEventGraphの方に並べてみます。
ドロップするといつもと様子が違います。白いピンがありません。
ここで、計算した座標を受け取る変数 CursorDestinationPos を追加してつなぐと下のようになります。この変数は当然Vector2D型です。
Destinationは目的地という意味で名付けました。
カーソルの座標は計算することができたので、次にその座標をカーソルに反映させないとカーソルの位置は変わりません。そこでカーソルを指定の位置に表示する関数を用意します。中身はいたってシンプル。SetCursorPos関数。実際にはスペースは入れていませんが、エンジンが勝手に、単語の間にスペースを入れてくれます。
これもつないでいきます。ポジションも決まったので、表示しちゃいましょう。
ここでいったん画面を確認してみましょう。
出現の演出が終わってからカーソルが表示されて入ればOK。
上の画像はFocusIndexの値に5を入れています。
カーソルが無事に指定した場所に出るようになったので、次にサムネイルのフォーカスを切り替える関数を用意します。
Integer型の引数を一つ受け取って、サムネイルの配列からGETノードで取り出して、サムネイルのWidgetに用意しておいたフォーカス状態にする関数をつなぎます。
これをEventGraphの方につなぎます。引数にはFocusIndex変数をつないでやります。
これでようやく指定した場所にカーソルが表示されて、さらにその指定のサムネイルがフォーカス状態になるというところまで来ました。
いよいよキー入力に合わせて移動させます。
カーソルを動かす
ここまでは、作り手が決めた通りに表示の段取りが進んできましたが、カーソルを動かすとなると、ユーザーの好き勝手なキー入力に応えるしくみが必要です。そのためには常に待機していてユーザーのキー入力を見張っておくことになります。それを実現するのがEventTickノードです。
Widgetブループリントに最初から置かれているノードで、ここにつないだノードは再生すると、再生を止めるまでずーっと処理され続けます。なので、ここでユーザーのキー入力を見張ることにします。見張ると言っても、最初からではなくサムネイルの入場演出が終わってカーソルが出現してからにしたいので、フラグを使います。
新しくBoolean(ブーリアン)型の変数を一つ追加して、さっきのカーソルのフォーカスをセットする関数の次につなぎます。isActive という名前にしました。チェックを付けてます。
ここチェックを付けるということは、それ以前はチェックが付いていないということなので、初期値は付けないでおきます。
Boolean型の変数は True(真) と False(偽)の2つの状態を扱います。スイッチみたいなものです。
チェックが付いている → True(=スイッチON、または「フラグが立った」)
チェックが外れている → False(=スイッチOFF、または「フラグが寝た」)
再生開始時は外れていて、カーソルが出現したらチェックが付くことになりました。
この変数の変化をさきほどのEventTickで見張るのです。
Banch(ブランチ)ノードをつないでそのCondtionのピンに変数をつなぎます。ブランチは枝(→分岐)という意味で、True ときとFalse の時で流れを変えることができます。今回はTrueに続きのノードをつないで、Falseには何もつながないでおけばいいのです。
さて、このTrueのときにつなぐのが、キーを押したか押してないかをチェックする処理です。以前の記事で紹介しているので、今回は説明を省きます。
このマクロライブラリを用意しておくと便利ですよ~ということで、始めたキャラセレを作ってみるという企画だったのを改めて再認識したところで次に進みましょう。
このマクロをTrueのピンにつなぎます。
GetPlayerControllerノードが必要なので引数としてつなぎます。
ユーザーがキーを押した瞬間に押したキーのところにつないだノードが処理されます。カーソルの位置とフォーカスの位置はFocusIndexという変数で指定できます。つまりこのFocusIndexという変数の値をどうにかすればよいのです。ここで改めてサムネイルの並びを確認してみます。
カーソルを右に動かす場合、Indexの値は +1すれば良さそうです。逆に左に動かす場合は -1 すれば良さそうです。問題になるのはそれぞれ端っこにいる場合です。たとえば右に動かす場合、3の位置にいるとき、+1 するとカーソルは 左下の位置に移動して、7の位置にいるときは 8 になってサムネイルの数をオーバーしてしまいます。逆も同じです。
この辺りの問題を念頭に入れつつ、Indexの値を増やしたり減らしたりする関数を用意します。
まずは単純に右に移動する関数から。
その前に、サムネイルのボタンをアンフォーカスする関数を用意しておきます。
先に用意した関数と同じ構造です。指定したサムネイルの配列からGETで取り出してアンフォーカスのイベントを呼び出すだけです。
で、右に移動したときの関数は以下のようになります。
計算で解決する方法もあるのですが、説明がややこしくなりそうなので、ベタな方法ですがSwitchノードを使いました。最初にフォーカスしていたサムネイルを元にアンフォーカスし、その直後に数値を見て分岐する Switch on Int ノードで切り分けています。切り分けた結果は大きく3つに分かれます。素直に+1 するものと 右端で+1下ときのものです。その結果を一旦ローカル変数TempIndexに入れます。Switchノードで道が分かれるからこそできる手法です。どのルートを通ったかで、TempIndexの内容が変わっています。それを再び FousIndex変数の入れて値を書き換えます。新しくなったFocusIndex変数の値でサムネイルボタンをフォーカスして終了です。
同じように左に移動したときの関数を用意します。複製するとラクちんです。
違いは、Switchノードの先の分かれ道ですが理屈は同じなので、サムネイルのIndex番号がどのように並んでいるかを確認しながらつなげばそんなに難しくはないはずです。
上の足し算ノードですが、ここに -1 が入っていることに注目です。引き算ノードにして1を入れれば結果は同じですが、関数を複製したのであればノードを入れ替えなくていいので楽です。1を引くのも、-1を足すのも同じだからです。
ちょっと話はズレますが、普通の変数とローカル変数に違いについて。
FocusIndex変数はどの場所からでも使用できて、しかも中の値は共有します。変な例えかもしれないですが、アチコチにあるATMからお金出し入れする感じに似てると思います。結果的に変動するのは一つしかない口座の残高。それに引き換えローカル変数の場合は、関数毎に小さな口座を開設することになるので同じ名前の口座名だけど関数が違うと中の残高も違うという使い方ができます。
この2つの関数をさっそくEventGraphの方につないでみましょう。
再生してみると、サムネイルのフォーカスが切り替わっていくのが分かると思います。
端まで行ったら反対側に移動していればSwitchノードが正しく機能している証拠。
この調子で今度は上下の分を用意します。今回のキャラセレは2段しかありません。言ってしまうと、上下どちらにいようとも、上下のキーどちらを押そうとも上下の場所を入れかえればいいだけです。
それぞれの段で ±4 すればいいだけです。
というわけで、EventGraphでつなぐときはUpとDownをまとめてつなぎます。
これでフォーカスの切り替えは完璧です。
最後に要のカーソルを移動させましょう。
カーソルを指定の場所に表示する関数は用意してあります。さらに目的地を保持する変数も用意済みです。FocusIndexの値はすでにキー入力に合わせて変動しています。
カーソル移動のための関数を用意します。
しくみについては過去記事を貼っておきます。
この関数をEventGraphのマクロにつなぎます。
最期のNo Input は何も入力がなかった場合に流れてくるところで、ユーザーがキーを押さなければ常に実行されます。
FocusIndexが更新されてフォーカス位置が変わっても、CursorDestinationPosの値が更新されていません。目的地の更新がないとカーソルは動かないので、更新するようにします。
これでカーソルの動きは完成です。
EventGraphはだいたいこんな感じになりました。
再生して触ってみましょう。
フォーカスしたサムネイルを追いかけてカーソルがするすると動きます。
今回は以上です。
次回は、フォーカスに合わせて大きいイラストを表示するところを作っていきます。
ではではステキなカーソルライフを!
キャラセレを作ってみる サムネイルを並べる
どうもです。まだ続いてますよ~。
あまり間が開かないようにしたいのですが、なかなか時間がとれなくてペースが悪いですが頑張って更新していきます。UE4でUIを作ることになった初心者の方向けに、オペレーションを幾分丁寧に書いてるのもありますが、そろそろ扱いに慣れてきている頃合いだと思うのでスピード上げていきたいと思います。
サムネイルを並べる前に、いったん過去記事のリンクを並べておきます。
タイトル名がいつの間にか変わっていたのに気づいて修正しました。
今回サムネイルを画面に並べるところを作ります。
コンテンツブラウザを見ると、WidgetBlueprintアセットが2つあります。2回目の記事で背景を置いたWidgetを編集します。
Wrapボックスを使ってみる
編集モードをDesignerに切り替えて、キャンバスにWrap Box(ラップボックス)をドロップします。
Wrap BoxはPaletteタブのPanelカテゴリの中にあります。
キャンバスの中で大体の位置を決めたら、設定を変更します。
サイズは、256x256のサムネイルが4つ横に並ぶので1024、縦に2つ並ぶので512にしています。
この WrapBoxはブループリントから触るので、isVariable のチェックボックスにチェックを付けます。名前は適当で構わないですが、ブループリントで扱う場合パーツの種類で動作や扱えるノードが変わってくるので、なるべくアンダーバーより前は名前を残すことをおすすめします。なので、上の画像だと、「164」 の部分をリネームする感じです。
ちなみに isVariable にチェックを付けるとキャンバスに置いているパーツのリストが変化します。
ブラケット[ ]が外れます。小さな変化ですが、知っておくと後で以外に役立ちますよ。
これでキャンバスの準備は完了です。
編集モードをGraph に切り替えます。
Initialize関数を作る
イニシャライズと読みます。役割はこれから仕事をするための準備をするところです。機能の多いものを作ろうとすると、何かと下ごしらが必要になります。UIを作っていると後から機能が増えたりすることはよくあることで、準備するものもまた後から増えることになります。なくても何の問題もないですがオススメしたいので作っていきます。
まず関数を新しく追加します。名前は Initialize とか Init でOK。
次にサムネイルの並びを設定する配列変数を新しく追加します。
配列変数は VriableType のプルダウンメニュー右側にあるアイコンをクリックして切り替えることで簡単に変更できます。
キャラクターはID番号を振って管理するのが一般的です。
分かりやすく0~7の整数で管理したいので、Integer型で用意します。
できた配列変数をさっきのInitialize関数の中に Set でドロップしてつなぎます。
左右に四角いピンがあります。値を入力したいので、左のピンからドラッグして、Make Array ノードを取り出します。
取り出せたら、Add pin + をクリックして[7]まで増やします。
こうなります↓
値が全部 0 になっているので、ここにキャラの番号を入れていきます。
サムネイルのテクスチャは ↓ のように並んでいるので、
数字が重複しないように入力します。順番はお好みで。
0~7を入力し終わったら、関数はいったん編集完了です。
EventGraph に戻ります。
Event Construct という赤いイベントノードに、できたてのInitialize関数をつないでやります。
よくみたら、関数のノードなのにイベントと同じアイコンになってます。ちょっと難しくなるのですが、関数は仕事をしたら結果や成果を報告することができます。戻り値とか言うとプログラマっぽくなりますね。ノードの右につく出力ピンにあたります。それがあればfのアイコン。なければイベントのアイコンになります。
WrapBoxにサムネイルを追加する
次にいよいよサムネイルを並べる関数を作ります。
ここに For Each Loop ノードをつなぎます。(キーボードの F を押しながらクリック)さらに先に作っておいた配列変数をGetで取り出して ForEachLoopノードにつないでやります。
この「ふぉーいーち」ノードは配列変数のために用意されているノードで、配列変数に仕込まれた値のぶんだけ回しますよ。という働きをします。今回用意した配列変数は [0]~[7]まで値が入っているので、8回だけ回してくれます。ここでいう「回す」というのは、右上の LoopBody と書かれた実行ピンの先につながっているものが対象です。配列変数の中身の分だけ回すと、右下のComplete のピンから出てきます。
次にCreate Widgetノードを取り出して、Loop Body の実行ピンにつなぎます。
ノードのClassのところに、サムネイル用に用意したWidgetをセットしてやります。
セットしたら、CreateWidgetノードの右に ReturnValue ピンがあるので、このピンの上で右クリック 、メニューから Promote to Variable を選択します。
新しく変数が作られるので、
これをいったんグラフから削除(Deleteキー)します。
消しても左のVariableタブの中にはちゃんと残っているので、これを配列型に切り替えます。名前も変えておきます。WD_Buttonsにしました。
改めてこの配列を Get でグラフにドロップします。
この配列は空っぽです。そこでForEachLoopで8回CreateWidgetした結果(ReturnValue)を配列に追加するのでAddノードをつなぎます。Addノードは配列専用のノードで配列変数からドラッグして探すとすぐに見つかります。
この配列は、後から個別にサムネイルのボタンを制御するために利用します。
いよいよ WrapBox に追加します。
isVariable のチェックを付けると、Variableリストの中に、WrapBoxが変数として存在するようになるので、これをグラフに Get でドロップします。
ドロップしたら、そこから Add Child Wrap Box ノードを取り出して下図のようにつなぎます。
さらに、サムネイルを作ったときに用意したセットアップ用の関数を取り出してつなぎます。この関数は、サムネイルのWidget内にしか存在しないので、ReturnValueからドラッグした場合だけ検索できます。
この関数にはパラメータ(引数)を渡せるように作っていました。
なので、ピンに値を渡せるようにつなぎます。
ForEachLoopから出てくる値は2つあります。
Array Element は、 配列の中身(Initialize関数で Make Array で仕込んだ中身)
Array Index は、順番(0~)
まだパラメータのピンが残ってますが、早く表示してみたいので、次に進みます。
これで、WidgetがCreateされて、作ったそばから配列に積んでいって、WrapBoxに子供として追加して、最後にサムネイルの関数に必要な値を渡す仕事が1セット。合計8セット行われるようにできました。
この仕事が終わったらサムネイルの出現用のアニメーションを呼びたいので、下図のようにつなぎます。
Completedのピンから、また次のForEachLoopをつないで、サムネイルの中のReady関数をつないでやります。仕上げにReturnノードをつないでおきます。
だいたいできました。
ここで、EventGraphに戻って、この関数をつなぎます。
これでコンパイルして保存します。
レベルを用意する
Widgetを表示するためには、レベルを用意する必要があります。レベルはコンテンツブラウザから作成します。
適当に名前を付けたら、ダブルクリックします。
レベルを開くとビューポートの右下が変化します。下は”Select”という名前のレベルを作った場合。
Persistant というのは今編集対象のレベルを意味しています。
ここが切り替わったのを確認したら、レベルブループリントを編集します。
レベルブループリントの編集方法は少し変わっていてエディタの Blueprints ボタンの右にある小さな▼をクリックしたらコンテキストメニューが出てくるので、この中からOpen Level Blueprint を選びます。
グラフエディタが開いたら、Event BeginPlay ノードに Create Widget ノードと、Add to Viewport ノードをつなぎます。
CreateWidgetノードの、Class のところは、今回作ったメインのWidgetをセットします。
コンパイルして保存しておきます。レベルは保存の方法も少し変わっています。ビューポートの上にあるSave Current ボタンから保存します。
レベルが用意できたので、さっそくPlayしてみましょう。
保存の時と同じくビューポートの上にあるPlayボタンをクリックします。
うまく表示されるでしょうか?
おや? いつまで経ってもサムネイルが表示されません。Escキーを押して再生を止めます。
わざとらしいことしてごめんなさい。原因はサムネイルWidgetに仕込んだ Set Timer by Event ノードです。このノードは数値を受け取ると、受け取った時間だけ待機してその後つながったイベントを実行します。 実は 0 を渡すと仕事をしなくなるのです。
気を取り直して、先ほどの関数を編集します。
サムネイルのSetupButton関数に0以外の数値を入れて再生してみましょう。
コンパイルしてエラーがないのを確認したら保存してPlay・・・
ようやく表示されました。
ただ一斉に出てきて面白くないので一工夫します。このままだと、わざわざDelayTime入れた甲斐もないです。
ふたたびさっきの関数を編集します。
まずローカル変数を用意します。左のMyBlueprintタブの中に Local Variables というカテゴリがあるので、そこに追加します。
DelayTimeは小数点で扱うのでFloat型です。ちなみにTempというのは「テンポラリ」の略で、「一時的な」という意味でよく使われます。このローカル変数はこの関数内だけで活躍します。関数の外には一切出ていけず一生をここで過ごします。さらに関数が仕事を終えるとこのローカル変数はメモリから破棄されて消えてしまいます。
実はそこがメリットだったりします。
で、さっそくつないでいきます。ローカル変数とはいえ扱い方は普通の変数とまったく同じです。グラフに ドロップして使います。
GetとSet の2タイプ をドロップします。
間にFloat 同士の足し算 をするノードを挟みます。
慣れないと解りにくいかもですが、
まず自分の値をGetして、そこに値を足して、自分自身の値を更新(Set)しています。
この処理を、Add Child Wrap Box の後に 瞬間的に行います。そしてすぐその値を渡しています。
足し算は適当にいろいろな数値で試してみてください。ちなみに 1.0 は1秒と同じ長さです。
これで完成です。
コンパイルして保存したら、再びPlayして確認してみましょう。
しゅるしゅる~
やった。うまくいきました。
やっぱりこういうインタラクションがあるのと無いのとでは印象が変わってきます。さらに視線誘導にも役立ちます。
今回はここまでにします。
なかなかのボリュームになってしまいました。
サムネイルも表示できたので、次回はカーソルの登場です。
ではではステキなサムネライフを!
キャラセレを作ってみる サムネイル編2
前回のつづき
Widgetブループリント
サムネイルの絵とアニメーションが用意できたのでいよいよブループリントを触っていきます。編集モードをDesigner から Graph に切り替えます。
まずは必要そうな変数を2つ用意します。
ひとつは自分自身の番号を保持しておくという役割。セレクトするキャラ8体。0~7のボタンの番号なので Integer型(整数型)です。
もう一つは演出用で、アニメーションの再生開始を遅らせるための時間を受け取る役割です。時間は小数点で扱うので、Float型(浮動小数型)にします。
変数は、エディタウィンドウ左の +Variable ボタンをクリックしていくつでも増やせます。作ったら名前(VariableName)をつけて型(VariableType)を変更します。
次に関数を用意します。
関数も変数のようにエディタウィンドウ左の +Function ボタンで増やせます。
クリックするとグラフが関数を編集する状態に切り替わります。
関数は作ったらまず名前を決めましょう。名前が役割を表します。今回作るこの関数はサムネイルとしてID番号と演出時間を受け取ってサムネイルの絵を切り替えるという仕事をします。なので、setupButton と名付けることにします。
名前は、リストに新しく増えた New Function 0 のところでファンクションキーの F2 を押すか、ダブルクリックするか、右クリックするかで変更できます。
変更したら次に引数(パラメータ)を受け取る準備をします。関数は必要な情報をもらって仕事をすることが多いです。グラフの真ん中に鎮座する紫にノードをフォーカスした状態で、Detailsタブにある、Inputsという項目の右端にある + ボタンを3回クリックします。
変数を作ったときのように名前と型をセットします。
この名前はピンの名前になります。
引数のピンから値が渡されてくる想定なので、先に用意した変数にとっとと入れる想定にしてやります。Variablesのリストからグラフにドラッグ&ドロップします。Get か Set か訊かれるので、ここは Set を選びます。(Altキーを押しながらドロップするとラクちん)
これをつなぐとこうなります。白いラインが処理の順番でそれ以外がデータ(値とか)の移動を表しています。
つぎに、残された引数のピン CharaIDをつなぐ先を召喚します。キャンバスに置いたImageパーツが、Variablesリストにいるはずなので、これをドロップします。今度は Get を選択しますす。(Ctrlキーを押しながらドロップするとラクちん)
置いてらノードの右についているピンからドラッグして、dyna と検索します。
Get Dynamic Material ノードを取り出したら、さらのその右にあるピンReturnValueから ドラッグして、sca と検索します。
SetScalarParameterValueノードを取り出したら、さきほど設定した引数の CharIDと結んでやります。
SetScalarParameterValueノードで忘れてはいけない設定があります。ParameterNameです。これはマテリアルを作ったときに決めた名前になります。
ここを間違うとうまく働いてくれません。
これで、サムネイルの絵をセットアップする関数は完成です。
この関数は親にあたるWidgetブループリントから呼び出されます。呼び出される際に引数に値が渡されてきます。
次に作ったアニメーションを再生するイベントを用意します。
編集ウィンドウのタブを、関数から EventGraph に戻します。
グラフの何もないところで右クリックして、AddCustomEventノードを取り出します。
適当なに名前を付けますが、このイベントが呼ばれると表示を開始するので、Ready と名付けます。この白いピンからドラッグして Set Timer by Event ノードを取り出します。
このノードの Timer ピンに、最初に作った変数 DelaySpawnTimeをGetでつなぎます。
Set Timer by Eventノードは、指定した時間が経ったら、イベントを実行するというタイマーの役割をします。なので、新しくカスタムイベントを追加してつなぎます。
Add Custom Eventノードを取り出して、Go という名前にしました。ここでアニメーションを再生させます。Variables の中に Animationsという項目があるのでここから作っておいた登場アニメーションをグラフにドロップします。
Getでドロップしたら、PlayAnimation ノードを取り出してつなげば、このイベントは完成です。
これで登場演出のイベントが用意できました。
次にサムネイルを選択肢のボタンとして機能させるためのイベントを用意します。また、カスタムイベントを新しくグラフに追加します。これはカーソルが乗ったときのフォーカスイベントになります。
上の Image_Focus というのは、フォーカスアニメーションを付けたImageパーツです。そのパーツに対して Visible(= 表示する)という指示をして、フォーカスのアニメーションを再生しています。
ここで表示しているということは、普段は表示されていないということにしたいので、Designer に戻って設定を非表示にしておく必要があります。フォーカス用のパーツを選択した状態で、Behavior > Visibility を探して、 Collapsed にします。
再び編集モードを Graph に戻します。
最後にカーソルがいなくなってフォーカスが外れたアンフォーカスイベントを用意します。
フォーカス用のImageパーツを非表示にして、ループアニメーションを止めます。
これで、最低限の機能を持ったサムネイルが完成しました。
作った機能をまとめると4つ。
- キャラのID番号をもらって絵を切り替える(関数)
- サムネイルが画面に登場するイベント
- カーソルが乗ったときのフォーカスイベント
- カーソルが離れたときのアンフォーカスイベント
実行されるとまず1が呼ばれ、続いて2が呼ばれます。あとは、3か4が適宜呼ばれます。ここでいう「呼ばれる」というのは、「働け」と指示されることでそのタイミングで処理が流れることになります。
こんな感じでUIの部品を作っていきます。
UMGでUIを作る場合、Designer で見た目のグラフィックを仕込んでおいて、Graph でイベント関連の処理を用意しておきます。これをなるべくシンプルな機能のみの部品(=アセット)として作っておくことで、画面作りと調整の効率が向上します。
サムネイルが完成したので
次回は画面にサムネイルを並べていきます。
ではでは ステキなカスタムイベントライフを!
キャラセレを作ってみる サムネイル編
前回の続き
ちなみにサムネイルのスペルは thumbnail ですよ皆さん。
選択用サムネイルを作る
まずはサムネイルの準備をします。テクスチャにはキャラクターの顔が複数並べてあるので、このままでは使えません。そこで部分的に切り出すためのマテリアルを用意します。
まずはマテリアルを作る
コンテンツブラウザにあるテクスチャアセット(赤い下線)のアイコンの上で右クリックすると、
コンテキストメニューが現れて、中に Create Material という項目があるので選択します。
できたマテリアルアセットのアイコンに適当な名前をつけて編集開始です。
すると最初からテクスチャノードがつながった状態になっています。
この仕様が地味に便利だったりします。
後からテクスチャアセットをドロップしても構いません。
さっそくUMGで使うための変更をします。
右端の大きな茶色のノードをフォーカスした状態で、ウィンドウ左のDetailsタブから設定をUMG専用に変更します。
UMGでマテリアルを扱う場合は必ず User Interface に切り替える必要があります。
Blendモードは、アルファチャンネル(透過)を使う場合は Tanslucent を選びます。アルファチャンネルが要らない場合は、Opaque(オペイク、不透明)を選びます。
設定するとグラフが変化します。つながっていた線が切れているのでつなぎ直します。
つぎに、テクスチャを部分的に取り出すための TextureCoordinateノード を取り出します。グラフの何もないところで右クリックして 下図のように検索するか、
キーボードの [U] キーを押しながら左クリックでも取り出せます。
取り出せたら下図のようにつなぎます。
赤いTextureCoordnateノードをフォーカスした状態で、左のDetailsタブから値を入力します。
UTiling と VTiling の値をそれぞれ 0.25、0.5 と入力して書き換えます。
する下図のようにTextureSampleノードのサムネイルが変化します。
どうなっているかというと、TextureCoordinateノードはテクスチャのタイリングを制御するノードです。このノードが最初に持っている値は 1.0 。これはテクスチャをすべてそのまま使うという意味です。それをより小さい値にしたことで結果的に一部だけを切り出すカタチになりました。テクスチャというのはUとVという座標系で管理されています。図で見てみると以下のようになります。
というわけで
TextureCoordinateノードは、テクスチャの一部分をトリミングするのに使います。
(設定する値が ”Tiling”なので、タイリングするときにも使えます。)
さてさて、 左上のUVを切り出せたら、つぎにUVを移動できるようにします。
TextureCoordinateノードから出てきた値に足し算をしたいので、Addノードを間に入れます。(キーボードの Aキーを押しながらクリック)
空いている B の入力ピンにつなぐのは UとVの2つの値なのでAppendVectorノードをつなぎます。
エラーがでますがほっておいて、ScalarParmeterValueノードを2つ取り出してつなぎます。(キーボードの Sキーを押しながらクリック)
このスカラーパラメータバリューというノードに、0.25とか、0.5とかの値を入れるとUVが移動しているのが確認できます。
これで進めてもいいのですが、せっかくなんで楽をしたいと思います。
参考までに以前書いた記事を貼っておきます。
できたのがコレ。
小数点の数値だけが書かれたノードは、Constant(定数)ノードです。キーボードの1を押しながらクリックすると取り出せます。固定の値を扱うときに利用します。
左端の Index と書かれたノードが、後から値を渡すための ScalarParameterValueノードです。これに値(0~7の整数)を入れてみてキャラがいい感じに切り替わったらマテリアルは完成です。左上の Applyボタン → Saveボタンと押して閉じます。
新しくWidgetブループリントを作る
サムネイル用に新しくWidgetブループリントを作ります。作り方は背景と同じです。
キャンバスに置くImageパーツは サムネイルのサイズ 256x256にします。
このImageパーツにさっき作ったマテリアルをセットします。
背景のテクスチャと同じ要領で、コンテンツブラウザで先にマテリアルを選んでおくと、矢印ボタンでクリックするだけでセットできてラクチンです。
アニメーションをつくる
サムネイルが登場と退場するとき、あとはフォーカスされている状態の3つのアニメーションを用意します。
UMGのエディタウィンドウ右下に、緑色の +Animation ボタンがあるのでクリックします。
クリックすると、すぐ下に New Aimation というのが現れるので、わかりやすい名前に変更します。この名前をフォーカスすると右側にタイムラインがアクティブになります。その状態でキャンバスに置いたImageパーツに動きを付けていきます。
今回は0.2秒で、回転しながら拡大するようにしてみました。
同じ要領で退場のアニメーションも作っておきます。
登場と退場ができたら、最後にフォーカスアニメーションです。登場と退場は無くても困らないですが、フォーカスしているかいないかは、UIにとってはとても重要な表現になります。マウスやタッチするタイプのUIではホバーアニメーションに相当します。
フォーカスのアニメーションは、サムネイルと同じ形で動かしたいので、サムネイルを切り出すために作ったマテリアルをDuplicate(複製)します。
マテリアルの複製を作ったらさっそく編集します。ほとんどいらないのでバッサリとノードを削除します。できたのがコレ。
TextureCoordinateの値はそのままです。上にあるのは Vector3 ノードです。
RGBのカラーを扱うときに便利なノードです。キーボードの 3を押しながらクリックすると取り出せます。これをUMGに戻ってセットします。
再びキャンバスに、Imageパーツを配置します。ZOrderの値はサムネイルよりも大きい数値にします。
ここに作ったフォーカス用のマテリアルをセットします。
この白い●をアニメーションさせます。カーソルが乗っている間ずっと点滅するので、アニメーションはループするように作ります。
アニメーションが3つできたらいよいよブループリントの編集です。
随分長くなったので、いったんここまでにします。
次回サムネイル編2でお会いしましょう!
ではでは
ステキなサムネイルライフを!