みつまめ杏仁

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

最近ハマったつなぎ方

ちょっとネタが無くなってきたのもあって更新ペースが落ちてます。

なので自分のウッカリでもさらしてみようかなと。

 

それは思ったような値にならず、原因も分からず、しばらく首をひねっていたつなぎ方です。

 

まずUMGでパーツを置いて、Widgetブループリントからいい感じにサイズを計算して調整しようとしました。

下図は 160x256 サイズの”Image”です。

f:id:hiyokosabrey:20161105010212p:plain

これを、下のようにつなぎました。

計算した値を別のところで使いたかったので変数に保存しています。

f:id:hiyokosabrey:20161105010316p:plain

ここで察しのいい方は気づかれていると思います。

Imageパーツのサイズをゲットして、タテ方向だけ1/16 にしてサイズを変更しています。

果たして、変数 Value の値はどうなっているでしょうか?

再生すると、パーツの見た目には うまくいっているように見えます。

でも、

答えは、 Value = 1.0  です。

 

え?

だって 256を16で割ったら16でしょ? なんで???

 

実際には 256 ÷ 16 = 16  したあとで、さらに 16 ÷ 16 = 1  となっているからです。

なんと割り算が2回計算されているのです。

 

SetSizeノードに割り算の結果が渡されたあとで、次は変数に値を渡すためにもう一度Imageパーツのサイズを取得して(このとき既に16になっている)16で割っています。
 

 見た目に、割り算ノードのあたりで計算が済んでいるように見えるのと、同じピンから取り出してるので、同じ値が出てくると思ってしまったのが原因でした。

 

正しくは以下。

f:id:hiyokosabrey:20161105044358p:plain

 

変数に値を入れない場合でも、値を確認しようとしてPrintStringノードをつなぐと・・・

f:id:hiyokosabrey:20161105045402p:plain

 1.0 という数値が表示されます。

PrintStringノードが値を表示しようとして、2回目の計算を行ってしまうためです。

 

では以下の場合はどうでしょう。

f:id:hiyokosabrey:20161105050009p:plain

 無事 16.0 という数値が表示されます。Imageパーツはというと問題なく 1.0 にならずに表示されています。

おそらくこれも2回計算していますが、PrintStringノードは、SetSizeノードのように結果を反映しないので、問題が起こらないのです。

 

この辺りのロジックが理解できるまでしばらくの間、首をかしげっぱなしでした。

 

以前に 乱数を発生させるノードでも同じようにハマったのを思い出しました。

例えば下のようにつないだ場合、

f:id:hiyokosabrey:20161105051632p:plain

画面には、2つの異なる数値が表示されます。

 

以後気をつけます。

 

ではでは

ステキなノーディングライフを!

スタッフロールをつくる 《完結編》

鶏肉は柔らかいのより噛みごたえのある方が好きです。

で前回の続き。

limesode.hatenablog.com

名前を表示するための器が用意できたところで、次に必要なのは中身です。

スタッフロールには関わった多くの人々の名前が載るので結構な行数になることが多く、それをブループリントの中で管理するのはさすがにちょっと躊躇われます。また、スタッフロール用の情報を集めて整理するのはExcelやテキスト系エディタの方が向いています。

というわけで今回CSV形式のデータを扱います。詳しくはヒストリアさんとこのブログにあります。

[UE4] CSVデータを扱う方法 DataTable編 | historia Inc - 株式会社ヒストリア

CSVは『カンマせぱれいてっどバリュー』の頭文字を合わせた用語で、簡単に言うと「カンマで区切った値」ということになります。Excelだと簡単に書き出せるのですが、Excelを持ってないのでテキストエディタで作りました。

f:id:hiyokosabrey:20161022231932p:plain

いくつかのデータをカンマで区切るのですが、今回4つのデータで構成します。

 

Index番号    表示タイプ     名称(文字列)   次の行までの待機時間(秒)

 

用意したWidgetは3タイプ。表示タイプを『グループ』『パート名』『人名』を0~2の数値で扱うことにします。で、できたのが以下

,NameType,NameString,NextInterval
0,0,STAFF,2.0
1,1,ROSE,1.0
2,2,Abigaile,1.0
3,2,Acropolis Romantica,1.0
4,2,<>Alphonse Dauder<>Anthony Meilland,1.0
5,2,<>Bienvenue<>Benjamin Britten,1.0
6,2,<>Cassandra<>Cioccofiore,1.0
7,2,<>Dorothy Perkins<>Gloria Mundi,3.0
8,1,DAHLIA,1.0
9,2,Anto Norma,1.0
10,2,<><><>Azumabeni<>Ben Houston<>Blookside Snowball,1.0
11,2,<><><>Devon Liam<>Freedom Fighter<>Gregory Stephenl,1.0
12,2,<><><>Happy Face<>Hoshitsukiyo<>Lavender Giantsl,3.0
13,1,TULIP,1.0
14,2,<>Dalvey Snow<>Cardinal Mindszenty,1.0
15,2,<>Finola<>Flaming Prissima ,1.0
16,2,<>Honoonomai<>Lambada,1.0
17,2,<>Maywonder<>Murasaki suisho,1.5
18,2,Page Polka,1.5
19,2,Ruby Prince,3.0

プレーンテキストなので Tab文字 を入れれば見やすくなりますが、文字として扱われてしまうのでCSVとして書き出す直前に置換して消すといいです。

文中の <> は分割するためのデリミタ用の文字です。

この内容で、拡張子 .csv を付けて保存するとUE4でインポートできます。

インポートする前に用意するアセットがあります。Structureです。

f:id:hiyokosabrey:20161023110627p:plain

ストラクチャーを作ったら適当に名前を付けて編集を開始します。編集と言っても変数を登録しておくだけのものです。

f:id:hiyokosabrey:20161023111454p:plain

表示タイプ(Int型) , 名称(String型) , 次までの待機時間(Float型)なので3つ。

4つのデータを仕込んだはずですが、今回 Index番号については特殊な扱いをするので変数にはしません。

追加したら保存して閉じます。

 

いよいよインポートします。作ったCSVファイルをコンテンツブラウザにドロップすると下のような小さなダイアログが出てくるので、先に作っておいたStructureをプルダウンリストからアセット名で選択します。

f:id:hiyokosabrey:20161023113903p:plain

OKするとDataTableアセットのエディタが開きます。

f:id:hiyokosabrey:20161023114218p:plain

問題なければ左上のFile メニューから 保存 して閉じます。

 

このエディタからも内容の編集が可能ですが、スタッフロールについてはReimportをオススメします。名前の収集がメールベースというケースがほとんどで、最後までExcel等で管理した方が修正も楽ちんでお手軽です。

Reimportは、コンテンツブラウザのアセットアイコンの上で右クリックすると簡単にできます。

f:id:hiyokosabrey:20161023115453p:plain

 

器と中身が揃ったので、表示する処理を作っていきます。

ブループリントアクターを使ってWidgetを表示します。

 

まずは、変数を2つ作って、Event Begin Play のところででセットします。

f:id:hiyokosabrey:20161023142246p:plain

 中央の見慣れない色の変数は、 UserWidget型の変数です。作る際にClass を選択するのがポイントです。

 f:id:hiyokosabrey:20161023142703p:plain

Make Array ノードをつないで、用意してある3タイプのWidgetアセットをセットします。

 

次に、メインとなるカスタムイベントを作ってつないでいきます。

まず最初に、DataTableアセットを取り込みます。

f:id:hiyokosabrey:20161023194843p:plain

DataTableを取り込むノードは2つ用意されているようで、getdata で検索します。

f:id:hiyokosabrey:20161023195217p:plain

Get Data Table Row ノードは Indexの値をName型に変換して、RowNameピンにつなぐとヒットすれば、その内容を取り出すことができるノードです。青いピンにまとめられているので、Breakeノードで分解すると変数ごとに振り分けられたピンが出てきます。それを使ってどんどんつないでいきます。

f:id:hiyokosabrey:20161023200056p:plain

図の Wd Credit Text (実際は wd_CreditText)という名前の変数は Create Widgetノードの 右にあるReturnValueピンから、一度 Promote to Variable(変数化)してから配列化したものです。今回インターフェイスを使ったしくみが活かされている部分がここです。

左の Create Widget ノードにはまだどのWidgetが入るか確定していません。確定していない証拠に ReturnValueピンから作った Wd Credit Text の型は User Widget型のままです。普通ならClassの部分にセットしたWidgetアセットの型になるはずです。そしてその型が確定しているからこそ中の関数やイベントを呼び出せるのです。ところが確定していないのに中に作ったイベント StartMove を呼び出すことができています。これがインターフェイスという仕組みなのです。StartMove ノードの右上に 封筒のアイコンがついてます。ノードを取り出す際に ”start” で検索すると取りだすことができます。

f:id:hiyokosabrey:20161023220838p:plain

インターフェイスを持っている相手なら、誰であるか特定できていなくても、そのインターフェイス経由で関数やイベントを呼び出すことができるのです。

おかげでノードのつながりがシンプルになります。

 

では続きを。

Widget仕込んだイベントディスパッチャーにバインドしています。これは「移動が終わったら連絡ちょうだいね」という部分で、連絡を受け取ったあとの処理を下の赤いラインの先で行っています。

f:id:hiyokosabrey:20161023221702p:plain

バインドインターフェイスの仕組みでできれば、スイッチノードで分けたりキャストしなくて済んだのですが、残念ながら無理でした。Set Timer by Event ノードは指定した時間がきたらEventピンにつないだイベントを呼び出すノードです。で、どこにつなぐかというと、先頭です。

f:id:hiyokosabrey:20161023222532p:plain

これで、Forloop などのノードを使わずに、イベントがイベントの終わりで自分自身を呼び出すカタチになります。このイベントが呼ばれると

  1. データテーブルから1行分のデータ取り出す
  2. 指定のあった名前表示用のWidgetを生成して配列に積む
  3. Viewportに追加してStartMoveイベントを呼ぶ。このとき名前のテキストを渡す。
  4. 移動完了の連絡を受け取るためにバインド
  5. データテーブルの指定通りにタイマーをセット
  6. Index変数を一つすすめておく

という流れで処理されます。

で、移動完了の連絡を受けたら、もうそのWidgetは用済みなので、メモリ節約のために削除してやります。

f:id:hiyokosabrey:20161023223954p:plain

先にViewportの方から削除します。スタッフロールは下から順番に出てきて順番に消えていくものなので、配列に積んだ一番古いやつ=先頭から消すことで新陳代謝できます。

ほぼ完成なのですが、このイベントを呼び出さないとイベントは動きません。

そこで Event Begin Play の最後につないでやります。

f:id:hiyokosabrey:20161023224549p:plain

 

ところで、このループはいつ抜け出るのか?という心配をされている方、ご安心ください。Get Data Table Row ノードが解決してくれます。

f:id:hiyokosabrey:20161023224919p:plain

Index という変数が肝だったのです。Name型にキャストして検索。見つかれば Row Found、見つからなければRow Not Found に流れます。これで、データテーブルから該当するナンバーが無くなったらこのイベントのループは終了します。

 

今回も長い道のりでした。

ようやくこの作業が報われる瞬間がやってきました。

では表示してみましょう。

空っぽのレベルを開いて、作ったこのブループリントをエディタのViewportにドロップします。場所は適当で構いません。そのままPLAYボタン押せばOK。

f:id:hiyokosabrey:20161023230020p:plain

・・・

なんかしみじみ。

自分の名前を入れてみるとより感動するかもしれません。

 

内容が変わったときは、リストを編集して再インポートすればすぐに確認できます。

新しいWidgetを追加する場合は、まず↓

f:id:hiyokosabrey:20161023230459p:plain

そして

f:id:hiyokosabrey:20161023230727p:plain

あとは、CSVデータの方に追加した表示タイプを入れればOK。

ロゴもWidgetを複製して改造すれば同じ方法でどんどん追加できます。

 

以上です

ではでは、ステキなスタッフロールライフを!

スタッフロールをつくる 《Widget準備編》

画面の下から出てきて上に消えていくだけの、どシンプルなスタッフロールを作ってみました。

f:id:hiyokosabrey:20161021233614p:plain

今回用意するアセットは以下の6種類8個。

f:id:hiyokosabrey:20161021233902p:plain

右のWidgetブループリント3つはほとんど同じ内容。

 

スタッフロールは、ただ名前が並んでるだけではなく、組織やグループ、セクション、パートなどの名称、協力会社さんのロゴ、俳優さんや声優さんなどは役名と対で表示したりと、結構表記のバリエーションを用意する必要があります。

今回は『グループ名』、『パート名』、『人名』の3種を作ります。

人名に関しては、1列~3列まで対応します。

 

まずは見た目の材料ということで、グループ名のWidgetから。

キャンバスの適当な場所にテキストブロックを一つだけ置きます。

f:id:hiyokosabrey:20161022000037p:plain

文字サイズは 少し大き目にして他と差をつけるために色を付けます。

f:id:hiyokosabrey:20161022000503p:plain

位置を調整する前に アンカー の設定を変更します。

f:id:hiyokosabrey:20161022000917p:plain

決定するとキャンバスの表示が変わります。

f:id:hiyokosabrey:20161022001352p:plain

次にSlotの中にある Anchors のパラメータを変更します。

f:id:hiyokosabrey:20161022001749p:plain

SizeY以外0.0 にすると下のようになります。Position Y を動かすと移動できます。

f:id:hiyokosabrey:20161022002112p:plain

最後に Is Variable にチェックを付けて、UMG編集完了です。

f:id:hiyokosabrey:20161022002605p:plain

 

次に

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

スクロールに必要なFloat型の変数3つ用意します。

f:id:hiyokosabrey:20161022003300p:plain

キャンバスに置いたTextBlockをグラフにドラッグ&ドロップしたら、Event Constructノードにつないでいきます。

f:id:hiyokosabrey:20161022004308p:plain

UMGで配置したものをブループリントから触る場合、Slot as Canvas Slot ノードを利用します。上のグラフは、画面下から出現して画面上に消えるまでの移動量を求めて変数に入れています。とりあえずフルHDモニタ想定で以下のイメージになります。

f:id:hiyokosabrey:20161022010533p:plain

今回選んだアンカーの設定にある PositionY は、0.0でスタートして最終位置は マイナスの値になります。そこで -1080 から サイズ(高さ)を引くことで、最終位置を求めることができます。720pだったら、 -720 から引いてやります。解像度に対して柔軟に対応する場合は、Get Viewport Size ノードがあるので、その値を利用するとさらに汎用性が上がります。

最終位置を求めるのにわざわざGet Sizeノードを使わなくても、動かしてみれば簡単に最終位置は判明します。これは、あとからサイズを調整する際、変数の値を変更しなくてもいいというのとマジックナンバーを使わなくていいというメリットがあるためです。気軽に感覚的にサイズを調整することができます。こういうちょっとした処理を仕込んでおくだけで調整の手数を減らすこともできるのでオススメです。

 

次に

移動が終了したことを伝えるためにイベントディスパッチャーを用意します。

f:id:hiyokosabrey:20161022090456p:plain

追加ボタンをクリックして名前を付けるだけでOK。

とりあえず onMoveEndと名付けました。

 

移動させる部分を作っていきます。

f:id:hiyokosabrey:20161022092617p:plain

Event Tick ノードにつないでいきます。

In Delta Time ピンからは フレーム毎に下図のような数値が流れてきます。

f:id:hiyokosabrey:20161022094432p:plain

フレームというのは映画でいえばフィルムの1コマにあたります。

これは 60fpsの場合ですが 1秒を 60で割った値とほぼ同じで、直前のフレームからの経過時間を表しているそうです。若干のバラツキはありますが、ここに60 を掛けてやれば、1フレームあたり 約1という値になります。なので 変数Speedに60が入っていた場合、1フレームにつき1ピクセル進むことになります。120が入ると2ピクセルになるのでスピードが上がります。

1フレームに進む移動量をY座標に足して、限界を超えたかどうかチェックします。

越えていなければY座標をテキストブロックに反映して次のフレームへ。

越えたらイベントディスパッチャーを呼び出します。

イベントディスパッチャーは ドラッグ&ドロップしてCallを選択したものです。

 

一旦ここで編集を終了して、ブループリントインターフェイスを作ります。

f:id:hiyokosabrey:20161022103631p:plain

適当に名前をつけて開いたら、右上に New Function_0 という関数があらかじめ置いてあるので名前を変更します。とりあえず StartMove と命名しました。

f:id:hiyokosabrey:20161022104211p:plain

続けて、下のDetailsタブから、Inputs(引数)を2つ追加します。

f:id:hiyokosabrey:20161022104203p:plain

これでブループリントインターフェイスは完成です。コンパイル→保存して閉じます。

 

さきほどのWidgetの編集を再開します。

編集モードをグラフにすると、エディタ上部にClassSettings というボタンがあるのでクリックしてDetailsタブの内容を切り替えます。

f:id:hiyokosabrey:20161022104816p:plain

DetailsタブにInterfaceを登録する場所があるので、Addボタンをクリックしてさっき用意したブループリントインターフェイスをセットします。

f:id:hiyokosabrey:20161022105340p:plain

追加できたら、グラフの何もな無いとこで右クリックして

 ブループリントインターフェイス内に作った関数を探します。

f:id:hiyokosabrey:20161022110526p:plain

 

関数名の頭にEvent がついている方を選ぶと以下のようなノードが出てきます。

f:id:hiyokosabrey:20161022110712p:plain

 右上にインターフェイスのアイコンがついています。

ここに最後の仕上げ。

f:id:hiyokosabrey:20161022111904p:plain

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

 

コンテンツブラウザからこのWidgetを2つ複製します。

複製した1つ目を開いてテキストブロックの見た目を変更します。

パート名用です。

f:id:hiyokosabrey:20161022112559p:plain

文字サイズを少し小さくして、カラーを変えています。

これで2つ目完成。

 

最期の3つ目は人名用。複製した残りのWidgetブループリントを改造します。

文字サイズとカラーを変更するのですが、ここで複数列の対応を追加します。

 

まずは編集モードをDesignに切り替えてキャンバスパネルを追加します。

その中に6つのTextBlockを入れて子供にします。

f:id:hiyokosabrey:20161022200151p:plain

アンカーの設定はそれぞれ以下のように変更します。

f:id:hiyokosabrey:20161022201434p:plain

レイアウトは下のような並びで配置しますが、

f:id:hiyokosabrey:20161022195833p:plain

実際には1行に重なった状態にします。

f:id:hiyokosabrey:20161022201649p:plain

テキストブロックの中は何も文字を入れないように空にします。

f:id:hiyokosabrey:20161022202229p:plain

あとはカラーと文字サイズ、字詰め方向(Justify)を設定したら、Is Variable にチェックを付けて編集モードをグラフに切り替えます。

 

String型と、TextBlock型の配列変数を用意します。

f:id:hiyokosabrey:20161022202800p:plain

TextBlock型は、Object Typesの中にあります。

f:id:hiyokosabrey:20161022202825p:plain

f:id:hiyokosabrey:20161022203044p:plain

6つのテキストブロックを配列に積みます。

移動するのは、キャンバスパネルになるので Slot as Canvas Slot ノードには、追加したCanvasPanelをつなぎます。

f:id:hiyokosabrey:20161022203413p:plain

インターフェイスで仕込んだ StartMove関数(ここではEvent)の部分を以下のように改造します。

f:id:hiyokosabrey:20161022204820p:plain

Parse Into Array ノードは指定した文字列(デリミタ)が見つかると分割して配列にしてくれます。

人名のWidgetはこれで改造完了です。

これですべてのWidgetブループリントが完成しました。

 

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

次回はデータの準備と表示する部分を書きます。

ではでは

 

 

 

わりと本気のコンボカウンター 《完結編》

いよいよ動作確認です。

前回の記事はこちら

わりと本気のコンボカウンター 《準備編》 - みつまめ杏仁

わりと本気のコンボカウンター 《UMG編》 - みつまめ杏仁

わりと本気のコンボカウンター 《Widgetブループリント編》 - みつまめ杏仁

 

一揃いのアセットができたので、テストしてみます。

テスト用のレベルを開いた状態で、エディタを開きます。

f:id:hiyokosabrey:20161009230625p:plain

 

まずはカウント用の変数を一つ用意。Int型です。

CreateWidgetから始めます。

CreateWidgetの ReturnValue ピンの上で Promote to Variable を選ぶと簡単に変数化できます。とりあえず表示位置を大まかに決めておいて、あとはバインド祭り。

f:id:hiyokosabrey:20161009231355p:plain

Widgetの中に仕込んでおいた イベントディスパッチャーにバインドします。

バインドは「紐づける」という意味合いです。四角いピンとカスタムイベントをつないでおくと、Widget内でイベントディスパッチャーがCall されたタイミングでここに通知が来て、カスタムイベントが作動するというしくみです。

CreateWidgetした側は、好きなタイミングでWidgetに干渉できるのに対して、CreateWidgetされた側からは逆の干渉ができません。そこで用意されているのが、このイベントディスパッチャーを使ったデリゲートインターフェイスという2つの仕組みです。

EventDispatcher をCallしてるノードと、Bindしてるノードにマウスカーソルを重ねると

f:id:hiyokosabrey:20161009234141p:plain

デリゲートという用語が出てくるのが分かります。

今回インターフェイスは使っていませんが、これもGUIにはとても相性がよいです。

 

つぎに、カウントアップの呼び出し部分。

スペースキーを押すとカウントするようにしています。

f:id:hiyokosabrey:20161009235039p:plain

最後にWidgetの関数に値を渡して完成。

 

再生してテストしてみましょう。

f:id:hiyokosabrey:20161010002537j:plain

f:id:hiyokosabrey:20161010002546j:plain

f:id:hiyokosabrey:20161010002552j:plain

f:id:hiyokosabrey:20161010002558j:plain

動画を貼りたかったのですが、直接はダメっぽいので諦めました。

 

消え方が派手だったり、確定した時の演出があると、最終のカウントがよりわかりやすくなります。

 プレイヤーは視界の端で見てるので、多少派手なくらいがちょうどいいです。

 

というわけで

わりと本気で作ってみたコンボカウンターです。

デザイナーだけでこのようなモックアップを作ってテストしてもらい、プログラマに組み込みを依頼する流れでいけば、小さなイテレーションループで済みそうですがいかがでしょう。

 

ではでは

ステキなカウントライフを

 

 

 

 

わりと本気のコンボカウンター 《Widgetブループリント編》

続きです。

 前回の記事はこちら。

 

わりと本気のコンボカウンター 《準備編》 - みつまめ杏仁

わりと本気のコンボカウンター 《UMG編》 - みつまめ杏仁

 

 

Widgetブループリント

絵的な素材が用意できたところで、いよいよブループリントに手をつけていきます。

まずブーリアン型の変数を一つ用意。これは、「HITs」と下敷きのアニメーションを初回と2回目以降とで分けるためのものです。これを 初めから置いてある、EventConstruct ノードにSetでつないで、初期値を false にしておきます。(①)

f:id:hiyokosabrey:20161008220412p:plain

最後に準備用の関数をつないでいます。(③)

Add to Viewport されるたびに、この関数が実行されるのは無駄なので、DoOnceノードを間に入れておきます。(②)

このセットアップ関数の中身は以下。

f:id:hiyokosabrey:20161008221211p:plain

キャンバスに置いたImageパーツはマテリアルにパラメータノードを仕込んでいるので、パラメータに値を渡していじることで、カラーやUVを変えることができます。そのためには、Get Dynamic Material ノードが必須です。このノードにはReturnValue という出力ピンがあるので、その内容を配列に積んでおきます。

扱い方が少し違うので、「HITs」だけは配列にはしません。

このGet Dynamic Material の操作は、マテリアルを操作する直前で行ってもいいのですが、私はReturnValue(戻り値)を変数化しておくこの方法をよく使います。

メモリは必要になりますが、なんとなく処理コストが安くなる気がします。

 

次に 新たに変数を3つ追加します。

f:id:hiyokosabrey:20161008223322p:plain

 

カウンターを表示する関数を作っていきます。

この関数はInteger型の値をひとつもらって、それを桁に分解して表示するという内容です。いつ呼ばれるかは、プレイヤー次第なのでわかりません。コンボ受付中はカウントアップするので問題ないのですが、受付が終了してフェードアウトの最中に呼ばれると問題です。かといってフェードが終わるのを待たせるのは、ユーザビリティ的に問題アリです。

そこで、フェードアウト中に呼ばれたら、フェードアウトのアニメーションを止める処理を入れます。

f:id:hiyokosabrey:20161008224348p:plain

別の場所でフェードアウト用のタイマーをセットしていて、そのタイマーをコントロールするためにTimerHandle型の変数を利用しています。

今は↓この部分です。

f:id:hiyokosabrey:20161008225159p:plain

 

次に受け取った数字を桁に毎に分解する関数を作ります。

一、十、百、それぞれの桁の有り無しを判定して、あれば0~9の数字を取り出し、なければ  -1 にします。

先に作っておいた配列変数 DigitNumArray に 桁毎の結果を入れていきます。

f:id:hiyokosabrey:20161009105209p:plain

桁を分解する方法は、割り算(÷)と剰余(%)のノードを使います。

 

f:id:hiyokosabrey:20161009110955p:plain

 Int型同士の割り算は、小数点以下は切り捨てになります。

 

 配列の中身(要素)を変更するのは Set Array Elem ノードを使います。

この Set Array Elem ノードですが、あらかじめ要素が存在しているのが前提です。

あらかじめ配列の要素を用意する方法はいくつかあります。今回はノードを使わずにセトしました。配列を作ったときの初期値です。一度コンパイルすると追加できます。

f:id:hiyokosabrey:20161009112958p:plain

ちなみに、Addノードは新しく要素を追加するときに使うもので、何度も呼び出される関数の中に置くと要素が増えることになるのと、追加する順番に注意を払う必要があります。

 

続きに、できた関数をつないで、さらにカウント数に応じてカラーを変える仕組みをつなぎます。

f:id:hiyokosabrey:20161009113748p:plain

関数内では、ローカル変数という使い捨ての変数を使うことができます。

カラーを計算して利用した後は覚えておく必要はないので(この関数の外では役に立たない)ローカル変数を新しく作っています。

今回のカウントは 999まで表示可能ですが、カラーは100以上で変化しないようになっています。精度と調整のしやすさを考えて0~200でクランプします。その値を InverseLerpノードで割合に変換します。

f:id:hiyokosabrey:20161009185924p:plain

例えば 100 だと InverseLerpノードは  0.5 を返してきます。↑の図で、ちょうど赤から紫に変化するあたりです。InverseLerpノードは 指定した範囲のどの辺にいるかを割合で返してくれる便利なノードで、マテリアルのパラメータとして相性ばっちりです。

上の例では、コンボ数が 20、40、60、80、100 を越えるごとに色が変わります。

紫色の次がいい色にならなかったので、100で変化が止まっています。

この辺は自由に加減してみてください。

 

というわけで今↓ココです。

f:id:hiyokosabrey:20161009190634p:plain

次に進みます。

各桁の表示部分です。すでに数字を桁毎に分解してあるので、配列からGetノードで取り出しつつ、アニメーションの再生をマクロを使って処理しています。

f:id:hiyokosabrey:20161009191235p:plain

マクロは同じことを何度もする場合にラクちんなので積極的に利用します。

上記のマクロは3つとも同じ内容ですが引数を変えています。

マクロの中身はこうなっています。

f:id:hiyokosabrey:20161009192113p:plain

さらにマクロがあります。

受け取った数字から テクスチャのUVを求めて、0未満(-1)だったら 0.0 を、0以上だったら 1.0 をパラメータにしてマテリアルにセットするマクロです。

f:id:hiyokosabrey:20161009192445p:plain

 Set Vector Parameter Value ノードはベクター型のパラメータで、LinearColorの形で値を渡します。セットアップ関数で作っておいた Material Instance Dynamic型の配列からGetノードで取り出してつなぎます。

 

これで数字部分の表示は完成です。

f:id:hiyokosabrey:20161009193809p:plain

 

次に HITs の表示です。f:id:hiyokosabrey:20161009194717p:plain

色の変更と、アニメーションの分岐、フラグのセットをしています。

 

この後、タイマーのセットをするのですが、その前に用意するものがあるので、いったんこの関数の編集を止めます。

 

Event Dispatcher(イベントディスパッチャー)を2つ用意します。

f:id:hiyokosabrey:20161009210637p:plain

コンボカウントの受付終了と、退場演出のフェードアウトが終わったことを通知します。通知する相手はこのWidgetを CreateWidgetしたブループリントになります。

 

次に 退場演出のイベントを用意します。

カウントアップの関数から EventGraph に戻ります。

f:id:hiyokosabrey:20161009211806p:plain

赤いのはカスタムイベントです。作ったEvent Dispatcherを2つとも投入です。

Event Dispatcherはグラフにドラッグ&ドロップして取り出します。その際選択肢が出てくるので、Call を選択します。

f:id:hiyokosabrey:20161009212247p:plain

ようやくTimerHandle型の中身が判明しました。フェードアウトのアニメーションが終了したことを通知するタイマーだったのです。

目覚ましが鳴る前に起きてしまって、うっかりタイマーを止めずに顔を洗っていると鳴り出してイラッ・・・みたいな状況を避けるためのものです。

 

ここで、先ほど編集を中断したカウントアップの関数に戻ります。

いよいよラストです。

f:id:hiyokosabrey:20161009213033p:plain

f:id:hiyokosabrey:20161009213619p:plain

CreateEvent ノードは、作成済みの関数やイベントを選ぶカタチになるので、いったん中断したというわけです。

 

おつかれさまです。これでWidget完成です。

次回は、このWidgetを呼び出すところを仕上げて終了です。

 

 

 

 

わりと本気のコンボカウンター 《UMG編》

わりと本気です。コンボカウンターです。ということで前回の続きで用意したマテリアルをUMGにセットしていきます。

 

limesode.hatenablog.com

レイアウトする

f:id:hiyokosabrey:20161009215321p:plain

Widgetブループリントを作ったらさっそくパーツを配置していきます。

全体の位置調整用にCanvasPanelをひとつ追加します。

f:id:hiyokosabrey:20161008191027p:plain

DetailsタブのSize to Contentにチェックを付けておきます。

f:id:hiyokosabrey:20161008191442p:plain

これは中身に応じてサイズを変えるという仕様になります。

 

続けてImageパーツをこのキャンバスの子供として追加します。

999の3桁まで表示するので3つ、HITsの文字に1つ、下敷きに1つ。全部で5つを追加して並べます。名前も変更します。Digit は「桁」のことです。

f:id:hiyokosabrey:20161008192242p:plain

DetailsタブのAppearance > Brush > Image から、それぞれのマテリアルをセットします。

f:id:hiyokosabrey:20161008192624p:plain

テクスチャに仕込んだ大きさに合わせてサイズを設定したら、いい感じに並べつつ、ZOrderの数値で重なりを調整します。

f:id:hiyokosabrey:20161008192341p:plain

配置は完了。

 

アニメーションをつける

次にアニメーションを作成します。

今回用意したのは5つ。

f:id:hiyokosabrey:20161008193231p:plain

動かすのは、Slot ではなく、Render Trasform を動かします。

f:id:hiyokosabrey:20161008194018p:plain

f:id:hiyokosabrey:20161008194026p:plain

自由にアニメーションを付ければOKです。

遅くても 0.25 秒以内がオススメ。

例えば最初の登場アニメーションはこんな感じです。→ [ _IN_Hits ]

f:id:hiyokosabrey:20161008194922p:plain

透明な状態から不透明にしつつ移動してきます。

これを毎回やるとちょっとウザイので、2回目以降のアニメーションを用意しています。→ [ _UP ]

 

今回退場は透明にしてフェードアウトさせました。

実はここが今回手こずった部分です。

f:id:hiyokosabrey:20161008200202p:plain

パーツをすべて透明にするのですが、このアニメーションを再生すると、コンボ数が1桁の場合でも、10と100の位の数字が現れてしまうのです。

かといって、桁数に合わせて同じような退場アニメーションを何個も作りたくない。

というわけで前回のマテリアルに仕込んだスイッチが役に立つのです。

 

UMG編はここで終了です。

次回はブループリント編です。

 

 

 

 

わりと本気のコンボカウンター 《準備編》

ちょっとガチなコンボカウンターを作ってみようと思い立って始めてみたら、意外に時間がかかってしまった。まぁほとんど寝落ちが原因だけど・・・

f:id:hiyokosabrey:20161008005130p:plain

コンボカウンターは格ゲーではお馴染みの表示です。アクションゲームやシューティングゲームでも見かけます。ただ数字でカウントするヤツですが、いざ作ってみると奥が深いんです。

今回作ったカウンターの機能は以下。

・登場と退場の演出

・最後の更新から一定時間待って更新されなかったら消える

・一定のヒット数毎で文字の色を変える

・999までカウント表示可能

f:id:hiyokosabrey:20161008010219p:plain

 

 

おおまかな処理の流れは以下。

 

①常駐系の表示物なので、まずは CreateWidget してBindだけ済ませておく。

このBind(バインド)は表示終了の通知を受け取ってViewportから削除するため。

 

②ゲームが始まって、いざ表示開始になったら、ここでAdd to Viewport 。

 

③変数をカウントアップしつつ、同時にWidgetの中の関数に渡して数字を更新。

 

④一定時間更新が無かったら、終了通知を送ってViewportから消しカウント用の変数をリセット。

 

あとはプレイ終了まで必要に応じて、②~④を繰り返します。

 

わりとボリュームがあるので、数回に分けて書いていこうと思います。

 

テクスチャの準備

主役となる数字。

サイズは 512x256

f:id:hiyokosabrey:20161008114046p:plain

↓はアルファチャンネル。

f:id:hiyokosabrey:20161008011513p:plain

インポート設定で、Clampにします。

 

f:id:hiyokosabrey:20161008114035p:plain

 

 ↓カラーチェンジ用  1024x32

f:id:hiyokosabrey:20161008012353p:plain

ちょっとサイズが大きめ(=256x128)ですが、色と範囲の調整がデザイナー的にものすごくラクチンです。色の境界がシャープになるようにインポート設定を調整します。

 

機能的にはこれで足りるのですが、視認性を上げるために下敷きを用意しました。

左がRGB、右がアルファチャンネルです。上部にあるグラデーションはオマケです。

f:id:hiyokosabrey:20161008123546p:plain

 

数字のテクスチャには仕掛けがあります。

まずはUVの範囲とサイズ。

f:id:hiyokosabrey:20161008102306p:plain

ピクセルの座標をUV値に変換する場合は、テクスチャのサイズで割ってやります。

タテならタテ、ヨコならヨコです。例えば ↑の図で128pxが2か所あります。

数字のタテは 128÷256 で 0.5、 HITs のヨコは 128÷512で 0.25 という具合です。

(いつもこの辺りを教えるとき、小数点で渋い顔をされる方がほとんどです)

左上を基準として、ピクセルの量をイメージすると分りやすくなると思います。UV値は左上が(0,0)で右下が(1,1)です。ようするにタテとヨコそれぞれで、場所やサイズを割合(%)で指定する感じです。

私はいつでも手元にソーラー電池で動く電卓を置いております。

 

さてさて

仕掛けというのは各文字の左と上に余白を空けていることです。

f:id:hiyokosabrey:20161008103845p:plain

8px分をキープ。この余白を使ってドロップシャドウを再現します。

 

マテリアルの準備

 インポートしたテクスチャから、マテリアルを作成します。

UMGで使用するので、MaterialDomain(マテリアル属性)はUserInterfaceを選択。

ちょっとゴチャっとしてますがこんな感じになります。

f:id:hiyokosabrey:20161008115943p:plain

テクスチャサンプルノードが3つありますが、一番上が、カラーチェンジ用で下2つが数字のテクスチャです。

真ん中ちょっと上の Constant2Vectorノード(キーボードの2を押しながらクリック)がドロップシャドウ用の数値です。8pxずらすので、UとVそれぞれで

8÷512= 0.015625、 8÷256= 0.03125 になります。

一番上の Tex Coordinate ノード(赤いやつ)のパラメータは、U方向に動かすのでかなり小さい値にしてます。とりあえず1ピクセル分以下です。『Tiling』はテクスチャの中から切り出すサイズだと思ってもらって問題ないと思います。1以上の値が入ると見た目に繰り返します(これぞタイリング)。

 

 次に「HITs」のマテリアル。

文字が固定でUVが変化したりしないので、少しだけシンプルになります。↑のマテリアルと共通する部分をコピペすると楽です。

f:id:hiyokosabrey:20161008122130p:plain

 

下敷きのマテリアルも用意します。

f:id:hiyokosabrey:20161008124336p:plain

テクスチャサンプルノードは2つもの同じものです。テクスチャの上の方にグラデーションを入れているので、その分ずらしたりしています。

 

 

テクスチャとマテリアルアセットの準備は以上です。

次回UMGでレイアウトするところを書いていきます。

ではでは