みつまめ杏仁

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

追加するとスクロールするやつ

 今回のぷちコンで作ったUIの中から、スクロールするリストについて書こうと思います。

f:id:hiyokosabrey:20180904120602j:plain

 ゲーム内でグレイマンがタイルを踏むたびに、画面下部のリストに文字が追加されていきます。画面いっぱいになると、文字が追加されるたびに、一文字ぶん左にスクロールアウトするという仕様です。

 実は結構ハマった部分なので、ちょっと難しい説明が出てきたりしますが、ひとまず中身について書いていきます。

 

 この仕様のためのWidgetは2つ。文字を指示通りに切り替えるだけのWidgetと並べてスクロールさせる表示用のWidgetです。

 制限時間のあるゲームとはいえ、追加し続けるとメモリを圧迫するのと、HorizontalBoxのサイズが大きくなりすぎて不具合が出たりしても怖いので、スクロールさせつつ端から文字を消していってます。こうすることでいくらでも足していけます。

 今回難しかったのは、この消しつつスクロールする処理の 『タイミング』 です。

 流れを図にするとこんな感じ。

f:id:hiyokosabrey:20180905210635p:plain

 

さっそく一つ目のWidgetから見ていきます。

キャンバスには文字と▼を配置。

f:id:hiyokosabrey:20180904134305p:plain

 今回文字については、ステージに並べたタイルで使っているテクスチャ(1024x1024)を使い回してるので、赤い × は別のテクスチャにしました。×はこのWidget専用でそんなに解像度も必要ないので、あらかじめキャンバスに置いておいて、文字とスイッチすることにしました。

 

テクスチャはこんな感じ。アルファチャンネルを合成表示してるので、ピンクのところは抜きになります。

f:id:hiyokosabrey:20180904140630p:plain

マテリアルでトリミングして差し替えるようにしています。

f:id:hiyokosabrey:20180904140413p:plain

 

WIdgetブループリントのキャンバスの配置は以上で、 EventGraph はというと、Event Construct のみ。

f:id:hiyokosabrey:20180905001658p:plain

Event Construct はカスタムイベントと違って(ビルトインというやつになるのかな)引数を設定することができません。なので、Int型の変数を一つ用意してExpose on Spawnの設定にします。このときInstance Editable も一緒にチェック付けないとあとで注意されます。

f:id:hiyokosabrey:20180905002101p:plain

この変数が文字の種類を数字で受け取ります。

数字と文字の対応はこんな風。

-1= ×

 0 = ぷ

 1 = ぶ

 2 = ち

 3 = さ

 0以下(ようするにマイナス)かどうかで、表示を切り替えています。

 これでこのWidgetは完成です。

 

次は並べる方のWidget

まずはキャンバスから。

f:id:hiyokosabrey:20180905002843p:plain

HorizontalBox を配置するのですが、クリッピング(見える範囲を限定する)のためにCanvasPanel の子供にしています。

この CanvasPanel の Clipping 設定を、Clip to Bounds に変更します。

f:id:hiyokosabrey:20180905003304p:plain

HorizontalBox の方は Inherit のままでOK.

これで CanvasPanel の外には何も表示されなくなります。

 

HorizontalBox の設定はこんな感じ。アンカーは左上。

f:id:hiyokosabrey:20180905003935p:plain

この HorizontalBox のアニメーションを用意しています。

f:id:hiyokosabrey:20180905004344p:plain

0.25秒で、-90移動させています。これは一文字ぶんの長さから。

f:id:hiyokosabrey:20180905004631p:plain

再生するとこうなります。

f:id:hiyokosabrey:20180905005006p:plain

CanvasPanel の外にはみ出させています。

 

キャンバスはこれで完成。次はブループリントの編集。

外から追加のリクエストをもらう形なので、カスタムイベントからつないでいきます。

f:id:hiyokosabrey:20180905192732p:plain

Create Widget ノードに、先に用意しておいた 文字のWidgetをセットしています。

その戻り値(Return Value)を、HorizontalBox に Add Child すると自動的に横方向に並べてくれます。

 

EventTickで スクロール処理中(isScrollingフラグ)かどうかをチェックして、スクロール処理していなければ、HorizontalBox が抱えている子供の数をチェックして一定数(今回は20個)を越えていればスクロールの処理を行います。

f:id:hiyokosabrey:20180905193318p:plain

上図の続き

f:id:hiyokosabrey:20180905194447p:plain

スクロールしたら、Remove Child して、ポジションリセット

するだけで いけるかと思ったんだけど、タイミング的な問題で アニメーションの再生を止める処理を追加しました。ここが今回てこずった部分です。

 最初原因が判らずいろいろ調べていたのですが、どうやらアニメーションの終了時間を取得してタイマーを動かしているのですが、その時点でまだアニメーションが終了しきっていないことがあるのです。アニメーションが終了しないまま、ポジションリセットを行って、アニメーションで上書きされて・・・ということが起こっていました。正常にタイマーと同期がとれるのは10回のうち1回あるかないかです。

 アニメーションとブループリントとで同じプロパティをいじっているから、このようなバッティングが発生しやすいのです。といっても数フレームでのタッチの差みたいなものなので、強制的にアニメーションを止めても、見た目にはわからない程度だというのもあり、上のような処理で落ち着きました。

 

 とりあえず以上で完成です。

 

 説明用のサンプルを動かしてみたのがこれ。

f:id:hiyokosabrey:20180905205218g:plain

 

 ちなみに、EventTrackで

f:id:hiyokosabrey:20180905200247p:plain

スクロール終了イベントを呼ぶようにしてみたけど、アニメーションの終了よりも、EventTrackの方が先に動くようなので、バッティングは避けられないようです。

 

 

ここからちょっと難しい話。

 アニメーションが完全に終了してから、次が呼ばれるのが理想ですが、プレイヤーのアクションによって動作する部分なので予測が難しいです。イベントドリブンな設計では、いつどのようなタイミングで呼ばれても問題なく動くようにしておくのが大事だなと改めて思いました。

 

 当たり前ですが、アニメーションには『時間』があります。想定の時間より短い間隔で呼びだされると、アニメーションが衝突することになります。

 UMGの Play Animation ノードは、実行すると必ずタイムラインの先頭から再生します。また、Set Timer by Event ノードも 再実行すると、残り時間が再セットされます。

なので、呼び出されたイベントで、

文字追加 → アニメーション再生 → 終わったら1文字削除とポジションリセット

としていると、短い間に何度も実行した場合に、呼び出された回数と一致しなくなるのです。

文字追加 は確実に処理されてるのに、 アニメーション再生 は再セットされてタイマー延長、という状態に陥るのです。

 例えば3回立て続けにイベントが呼ばれると、追加処理は3回、3文字追加されますが、アニメーションは1回です。

 

 演出が終わっていない間に受け付けたものはどう処理するかをよく検討する必要があります。と言っても選択肢はあまりないですが、

  • 無視する(先のやつ優先で後のやつ以降はなかったことにする)
  • 先のやつを止めて、改めて後からのやつを処理(後のやつ優先)
  • 一時的にスタックしておいて順次処理してゆく
  • 追加分として一緒にまとめる

くらいでしょうか。

 UI的に見せ方や重要度によって選択することになります。今回は3つ目のスタックするやつを実装しています。これは、文字を追加するリクエスト処理と、スクロールさせる処理を別々にすることで可能になりました。

 追加するだけさせておいて、EventTickでいつ追加されるかのタイミングに関係なく溢れが解消するまで、監視しつつスクロールを処理します。ゲーム的にある程度のリアルタイム性は維持したいので、アニメーションの尺を短かくしています。

 

f:id:hiyokosabrey:20180905211251j:plain

 

  今回の台風はなかなかでした。結局7時間ほどで復電しましたが、記事を書いている途中でも何度か瞬間的な停電があって、ノートPCさまさまと思ってたのですが、ルータが落ちたら記事の保存ができなくなるので、結局おとなしく嵐が過ぎるのを待つことに。夏場の長時間停電で暑いわ暗いわ冷蔵庫の中身は心配だわ、で結構精神的に削られました。電気って素晴らしいですね!

 

ではでは

今回はこの辺で。

ステキなUMGアニメーションライフを!

 

 

 

 

サウンドの再生管理にもEnumを使ってみた

 途中経過はいくらかTwitterで公開していぷちコン作品ですが、今日ようやく応募できました。一段落したのでブログの更新ペースが上がるはず。自信ないけど・・・。

応募の条件としてYoutubeとかニコ動とかに動画をアップするというのがあるので、応募に使った動画のリンクを貼っておきます。


【第10回ぷちコン応募作品】 ぷ→ち

 

 UI周りは今まで記事にしてきたネタを復習してみた感じなので、目新しいネタが無いですが、もし、「コレどうなってんの?」みたいなのあれば、コメントなど頂けるとブループリント晒しながら「これこのようになっておりまする。」と記事にさせていただきたく思う所存。

 

さてさて

今回の記事は、UIじゃないんですが「試してみたら以外に良かった」やつです。

サウンド関係については、完全に素人なので、手探りでの実装になったのですが、今のところお気に入りということで記事にして晒します。もっとベストな方法や、問題箇所の指摘などあればぜひ、ツッコミいただけるとありがたいです。

 

まず Enum を作るところから。

コンテンツブラウザの空いているところを右クリックして、

Blueprints > Enumeration を選択します。

f:id:hiyokosabrey:20180902214527p:plain

f:id:hiyokosabrey:20180902214757p:plain

適当に名前を付けて中身を編集します。

今回は2曲しか使ってませんが数が増えたらここで増やせます。

f:id:hiyokosabrey:20180902214819p:plain

次に、サウンド用ブループリント。

コンテンツブラウザで右クリックして、

Blueprint Class > AmbientSound

f:id:hiyokosabrey:20180902223512p:plain

f:id:hiyokosabrey:20180902223832p:plain

これを編集します。

 

ブループリントエディタが開いたら、ポーズ対策。

ClassDefaultから

f:id:hiyokosabrey:20180902224308p:plain

Detailsタブの Actor Tick にある Tick Even when Paused にチェックを付けます。

f:id:hiyokosabrey:20180902224333p:plain

これで、ゲームがポーズ中でもこのブループリントの Event Tick が止まることはなくなります。

この辺りはこちらのブログが参考になりました。

hogetatu.hatenablog.com

では、EventGraphへいざ。

鳴らしたいBGMのキューをSoundBase型の配列にセットします。

f:id:hiyokosabrey:20180902225636p:plain

こういった変数の《型》はどうやって調べるかというと、BGMを再生させる場合はPlaySound2D という再生用のノードが用意されていて、そいつの入力ピンから変数に昇格させると簡単に用意できます。

f:id:hiyokosabrey:20180902230853p:plain

f:id:hiyokosabrey:20180902231448p:plain

あとは、詳細タブ(Details)から配列に変更したりできるようになります。

 

曲を再生するための関数を用意しました。

f:id:hiyokosabrey:20180902232704p:plain

このブループリントには最初から AudioComponent が用意されているので、そのコンポーネントに対して再生の指示をします。がその前にどの曲を?となるので、上図のような構成になります。

関数の引数(Inputs)がポイントなのですが、上の青いのは AudioComponent型、下はEnum型です。ピンを追加して型を検索すると、先に用意しておいたEnumが見つかります。

f:id:hiyokosabrey:20180902233012p:plain

Enum型は名前(表示名)で扱ったり、Byte型という値(ほぼ数字)で扱ったりできる特殊なやつです。Int型に変換(キャスト)して使うと、 0~255 の正の数が扱えます。

曲を配列から選んで再生できる関数が用意できました。

ちなみにこの関数はこのブループリント内で利用します。

 

次に外から呼び出してもらうための各種イベントを3つ用意しています。

 

ポーズ中でも、曲の再生状態をチェックしたいので、

Event Tick を↓のような感じにしてます。

f:id:hiyokosabrey:20180902234957p:plain

再生中かどうかのチェックは、常にやりたいわけではないので、状況に応じて開け閉めできる Gate というノードを使用。

誰かがチェックするぜい! となったら、左にあるカスタムイベント wait を呼び出して ゲートを開けます。

すると Tickのパルスが流れ始めるので、今なってる曲が止まるまでチェックし続けます。曲が止まったらすぐにゲートを閉じてやらないと、Falseの先の処理が何度も走ってしまいます。

右端のは再生用に用意した関数です。

Enumを変数化しておいてつないでいます。中は鳴らしたい曲が保持されています。

 

次に、再生指示を受け取るイベント。

まず再生したい曲をEnum型で受け取って変数に入れています。

f:id:hiyokosabrey:20180903000009p:plain

Branchが2個もあってちょっとややこしい感じですが、機能としては3つ。

  • 何か曲が鳴ってなければ指定された曲を即再生
  • 何か曲が鳴ってたらそれを止めて指定された曲を再生
  • 何か曲が鳴ってたら、その曲が止まるのを待って指定された曲を再生

2つ目の Branch は イベントノードの 引数で分岐するようにしています。

 

最後は、フェードアウトするイベント。このノードは結構便利かも。

f:id:hiyokosabrey:20180903001000p:plain

これで最低限の仕様を持ったBGM再生用のブループリントが完成。

 

あとはこれを、ワールドにスポーンさせて、中のイベントを呼び出すだけです。

おっと、その前にAudioComonento の設定を確認。

コンポーネントリストから選択して、詳細タブをチェック。

f:id:hiyokosabrey:20180903001743p:plain

ポーズ中にも再生できるように is UISound にチェックを付けます。

f:id:hiyokosabrey:20180903002214p:plain

念のため Auto Activate のチェックを外しておきます。

f:id:hiyokosabrey:20180903002247p:plain

 

この辺りはこちらのブログを参考にさせていただきました。

 

unrealengine.hatenablog.com

UE4 サウンドキューを配置して再生する(Audio Component) 凛(kagring)のUE4とUnityとQt勉強中ブログ

 

曲を再生するとこはどうなってるかというと、あらかじめスポーンしたのを変数化しておいて、

f:id:hiyokosabrey:20180903003533p:plain

適当なタイミングでイベントを呼び出します。

下は、タイムアップになったので、曲をフェードアウトさせて、曲が停止したらリザルトの曲を流しなさい。というものです。

f:id:hiyokosabrey:20180903003306p:plain

 右端のやつは、別のEnumですが、Enumで扱うとアセットの名前とかじゃなくで、シチュエーションごとの名前とかで指示できるので、全体のフローを制御するときに分かりやすくなると思います。とりあえず「リザルト」の曲流しておいてよ。という指示でOKなのはとても気楽にセットできて安心できます。 

 実際に流す曲自体は曲の管理を担当するメンバーが、このブループリントとEnumを編集すればいいので、それなりに分業もしやすいのではと思います。

 あと、ランダムにしたり、一定の条件で曲を変化させたりといったことも、サウンドブループリントの方をいじればOK.。今回は曲の配列とEnumの順番を一致させていますが、一致させない場合は配列を使わなくても問題ないです。

 

 個人的に結構気に入ってるこの仕組みいかがでしょうか。

もっとナイスなベストプラクティスとかありそうですが、ひとまずこれを推してみます。

 

  今回ぷちコン作品を作るにあたっていろんなブログ様に助けていただきました。この場を借りてお礼申し上げます。ありがとうございました。

 

ではでは

ステキなBGMライフを!

 

 

 

 

 

 

[3D]頂点移動でタブウィンドウ的な何かをつくる《拡張版》

今年の4月にメッシュでできたウィンドウ的な何かを頂点変形させる記事を書きました。その内容に関してコメントいただきましたので解決方法を《拡張版》としてを書くことにしました。「まず横幅を広げた後で縦方向に広げたい」という案件です。

limesode.hatenablog.com

 

過去記事では、タブの水平移動と縦方向に広がるウィンドウを、メッシュの頂点にテクスチャでマスクすることで実現しました。その時のマスクテクスチャがこちら。

f:id:hiyokosabrey:20180428114908p:plain  64x32 の実寸です。

 

これに対して今回用意したのはこれ。

f:id:hiyokosabrey:20180831004548p:plain

 

拡大すると、こんな感じ。

f:id:hiyokosabrey:20180831004804p:plain

メッシュのUVが分かりやすくなるようにオレンジのラインを入れています。

左右に緑色の四角を塗って、レイヤーブレンドを『覆い焼き(リニア)』にします。

f:id:hiyokosabrey:20180831004931p:plain

こうすることで、青く塗った部分(R:0, G:0, B:255)を壊さずに緑成分を重ねることができます。

f:id:hiyokosabrey:20180831005111p:plain

赤や青同様にこの緑成分を横方向の拡張に利用します。

 

さてさて、

これをUE4にインポートしたら、マテリアルを改造します。

前回はこんな風でした。

f:id:hiyokosabrey:20180828235019p:plain

今回はここに、頂点のプラスマイナスを判定してから緑チャンネルと乗算する処理を追加します。

f:id:hiyokosabrey:20180831004151p:plain

まず最初に頂点座標のうち、X座標について IF ノードで判定しています。

この値とテクスチャの緑成分を掛け算することによって、動かしたい頂点と動かしたくない頂点を分けてています。

さらに

IFノードの判定によって出てくる、-1、 0 、 1 という3つの値を利用することで、あとから足し算する際の方向をそれぞれの頂点位置に合わせることができます。

 

 

f:id:hiyokosabrey:20180831010551p:plain

 

 

プラス方向に動いてほしい頂点には、プラスの値を足し算して、

マイナス方向に動いてほしい頂点には、マイナスの値を足し算する必要があります。

 

最初実験していた時、

テクスチャでプラとスマイナスの値を作って加算していました。

何となくいい感じだったのですが、タブが一緒に動いていました。

黒の部分の値が計算されてしまって、マスクがうまく効いてないという状況でした。

 

今のところ、テクスチャの役割としては、カラーのある部分の頂点を動かす、ということになります。

 

以上でマテリアルは完成です。

 

次にアニメーション部分の改造です。ブループリントは前回こうなっていました。

f:id:hiyokosabrey:20180430151329p:plain

ここに、横幅を広げるタイムラインと、ノードを追加します。

タイムラインに横幅変化用のトラックを一つ追加。

f:id:hiyokosabrey:20180829002931p:plain

下段が横幅用。上段が縦幅用。 値は 0 → 1.0 に変化しています。

トラックが追加できたら、イベントグラフの方。

f:id:hiyokosabrey:20180829003216p:plain

カスタムイベントの引数(Inputs)に横幅を受け取るピンを追加して、タイムラインの値と掛け算しています。

 

このカスタムイベントを呼ぶところで、パラメータをセット。

f:id:hiyokosabrey:20180829003557p:plain

これで準備完了です。

 

テストしてみましょう。

f:id:hiyokosabrey:20180831013228g:plain

なんとか、うまく動いてくれました。

ちょっとややこしい作りになりますが、うまく応用できればいろいろ表現の幅が広がりそうです。

いかがだったでしょうか?

最近、小ネタをTwitterの方に呟くようになったので、ブログの更新ペースが伸びませんが、何かあれば当ブログにコメントいただくか、Twitterの方からつついていただけるとありがたいです。

 

ではでは

ステキなウィンドウライフを!

 

「ぷち」っとタイマーを作る

UMGでタイマー表示をつくってみたので、「ぷち」っとご紹介。ぶっちゃけ「ぷちコン」用の制作メモみたいなものです。過去にテクスチャを使ったやつを記事にしているんだけど、今回はNo Texture です。UMGです。

 

タイマー表示にもいろいろあるのですが、今回は 100分の1秒を表示するタイプです。細かい数字がせわしなく動くと、急かされる印象を与えることができます。

さらに残り時間を感覚的に伝えるために、数字だけでなく円ゲージも合わせて動かします。

 

さっそく タイマー表示用のWidgetブループリントを作成。

キャンバスに TextBlock を3つ配置。 30秒想定です。

f:id:hiyokosabrey:20180816000112j:plain

外側のリングは、Image で、マテリアルをセットします。

マテリアルは以下。

f:id:hiyokosabrey:20180816000726p:plain

Value と書かれたノードは Scalar Parameter です。リングの太さや大きさを調整する場合は、Add ノードで、少しだけ値を足してやります。

f:id:hiyokosabrey:20180816001235p:plain

すると、プレビューウィンドウ内が↓のようになります。

f:id:hiyokosabrey:20180816001308p:plain

調整が終われば Add ノードは 削除します。

Valueの値が 0.0 ~ 1.0 に変化するとこうなります。

f:id:hiyokosabrey:20180816002304p:plain

 

ブループリントを編集していきます。

まず残り時間を管理する変数を3つ用意します。Float型です。

f:id:hiyokosabrey:20180816015607p:plain

0.2 を掛けているのは、残り 20%以下になったら点滅させるので、その判定用の変数です。

マテリアルのパラメーターをいじるので、早い段階でDynamic Material Instance 化しておきます。

f:id:hiyokosabrey:20180816003051p:plain

この場合、Add to Viewport した直後になります。

 

このWidgetが表示されてすぐにタイマーが動き出すのを防ぐために、Boolean型の変数を1つ用意してフラグとして使います。

 

カウントダウン開始は、他のブループリントからの呼び出しが合図です。

そのためのカスタムイベントを用意してフラグを立てるようにします。

f:id:hiyokosabrey:20180816004029p:plain

カウントダウン開始で「Ready?」 から 「Go!!!」に変えるのですが、少ししたらフェードアウトするように、アニメーションを作って再生しています。

f:id:hiyokosabrey:20180816004652p:plain

 

タイマーの心臓部を作っていきます。

f:id:hiyokosabrey:20180816005613p:plain

続き

f:id:hiyokosabrey:20180816012317p:plain

タイマーがゼロになったら、次の表示に進めるために、イベントディスパッチャーを用意してCall(呼び出し)でつないでいます。

f:id:hiyokosabrey:20180816012331p:plain

グラフにドラッグ&ドロップするときに選択します。

f:id:hiyokosabrey:20180816012527p:plain

 

表示更新用の関数はこんな感じ。

f:id:hiyokosabrey:20180816013044p:plain

続いて 小数部分。

f:id:hiyokosabrey:20180816013635p:plain

何をやっているかというと、小数点を境に数字を取り分けています。

例えば  23.4567 という値があったとしたら、まずは整数部分。

f:id:hiyokosabrey:20180816014204p:plain

次は 小数部分。

f:id:hiyokosabrey:20180816014856p:plain

最後は、リングゲージへの反映です。

f:id:hiyokosabrey:20180816015851p:plain

これで出来上がりです。

 

テストしてみます。

f:id:hiyokosabrey:20180816021013j:plain f:id:hiyokosabrey:20180816021023j:plain

f:id:hiyokosabrey:20180816021039j:plain f:id:hiyokosabrey:20180816021051j:plain

 

実際は、HUDクラスのブループリントを用意して、

f:id:hiyokosabrey:20180816022325p:plain

そこからカウントダウン開始のカスタムイベントを呼び出します。HUDブループリントの Event BeginPlay で、Create Widget して ReturnValue を変数化しておくのがポイント。

f:id:hiyokosabrey:20180816021835p:plain

で HUD用のWidgetをまとめて Add to Viewport します。

f:id:hiyokosabrey:20180816022458p:plain

タイマー作動させるイベント。

f:id:hiyokosabrey:20180816023024p:plain

カウントダウンが終了したらリザルト表示に移行。タイマー作動と同時にイベントディスパッチャーに対してバインド(紐づけ)しておけば、勝手に流れてくれます。

 

最終的に、このHUDのタイマー作動イベントを呼ぶヤツが必要になるのですが、何がトリガーになるかはゲームの内容次第なので、ひとまず今回はこの辺までにしておくことにします。

 

思ったより「ぷち」っという感じにならなかったですね。テキストの表示だけだったらシンプルに作れますが、ちょっと欲張ってしまいました。

 

ではでは

ステキなタイマーライフを!

 

 

 

 

 

 

 

オブジェクトをグリッド状に並べる

 毎日の暑さ辛いですね。近所で稲の葉先が黄色くなってる畑をいくつか見かけました。この熱エネルギーをやる気に変換するマシンか何かあれば、と思ったけど、やる気に溢れすぎて炎天下に飛び出したり、汗だくで何やら叫びつつ暴れたりする人のイメージが沸いてしまい、危険なマシンになりそうだったのでそのような妄想は忘れることにします。だれか早くクーラードリンク開発してください。

 さてさて、ぷちコンのお題が発表されましたね。今回は10回目ということで「ぷち」だそうです。私も何か作ってみようかと思い立ったので、いろいろ試していくことにしました。形になるかわかりませんが。

 

 

何となくタイルを並べてみることにしました。

プロジェクトのテンプレはThirdPersonです。

f:id:hiyokosabrey:20180812110240j:plain

いきなり動き回れて、カメラぐりぐり回せるので楽しいセットですよね。

このフロアをスッキリ片づけます。階段だけ残しました。

f:id:hiyokosabrey:20180812112657j:plain

最初から置かれているメッシュたちは、Mobility設定が、Static になっているので、ワールドから削除しても、床に影が焼き付いています。

f:id:hiyokosabrey:20180812111137p:plain

Buildツールの Build Lighting Only を実行すると、影が再計算されます。

f:id:hiyokosabrey:20180812112938j:plain

で、床のサイズと階段の場所とスタート位置を調整。

f:id:hiyokosabrey:20180812110708j:plain

レイマンの載ってる床はCubeを平たくして階段の下に置いています。そのままだと馴染まないので、階段と同じマテリアルをアサイン。

 

 

この床にブロックを並べてみようかと。

並べるブロックは生のStaticMesh だけだと面白くない(いろいろ遊びを仕込みたい)ので、Blueprint Actorにします。新しく Actorクラスのブループリントを作成。

f:id:hiyokosabrey:20180812114352j:plain

空っぽなので、AddComponent で StaticMesh を追加。

DetailタブにあるStaticMeshから、適当なキューブメッシュをセット。

f:id:hiyokosabrey:20180812114817j:plain

Viewportで確認しながら、サイズを調整したらひとまず完成。

f:id:hiyokosabrey:20180812114918j:plain

 

これを並べるためのブループリントを用意していきます。

新しく、Actorクラスのブループリントを作成。

並べるための関数を作ります。↓前半部分。

f:id:hiyokosabrey:20180812120535p:plain

引数(Inputピン)は Int型と、Float型の2つ。とりあえず 正方形に並べるので1辺ぶんの数と、配置間隔。

開始位置でいろいろ計算してるのは、センタリングするためです。

ForLoopで機械的に並べていくので、最初の起点となる座標さえ判ればあとは繰り返し処理だけでOKです。

例えば、ヨコに5個並ぶ場合、

f:id:hiyokosabrey:20180812135932p:plain

並べたい数 5 から 1つ減らした数が、オブジェクト間のスキマの数。これにスキマの距離を掛けると、端から端までの全体の距離になります。これの半分の距離を、中心から引いた場所が 起点 になるのです。

これでどんな数でもセンタリングできます。

 

 グリッド状に並べるのですが、引数は1辺の最大数を受け取って処理します。例えば5という数字を受け取ったら、 5x5 = 25 です。 UE4の ForLoopは LastIndexで指定した数字までカウントして処理します。0ゼロスタートだと、0~24 が正解。

f:id:hiyokosabrey:20180812141846p:plain

f:id:hiyokosabrey:20180812142022p:plain

コードで書くと

for ( int i = 0; i <= 24; i++ ) {   }

です。

 

for ( int i = 0; i < 25; i++ ) {   }

 だったらいいのに・・・

 

どうせ First Index のピンが空いてるんだから、 そこを 1 始まりにすればいいじゃん。

と思われるかもしれません。確かに回数的には問題ありません。(恐らくゼロ始まりに馴染みに無いノンプログラマ向けの仕様だと思います。Epicの優しさ?)

でも、このゼロ始まりの方が、この後のループ処理に便利なのです。

 

後半部分はこんな感じ。

f:id:hiyokosabrey:20180812143237p:plain

まずポジションの計算をするので、1 始まりだと、ズレたところから始まってしまうのです。

 

右端の Spawn Actor From Classノードのは、先に用意してあった、キューブの入ったブループリントをセットしています。

 

 

関数に並べる仕事をさせてみる。

関数にパラメータを渡して呼び出すと並べてくれます。

f:id:hiyokosabrey:20180812143649p:plain

Event Begin Play につないだら、コンパイルして保存。

ワールドにこのブループリントをドラッグ&ドロップします。

f:id:hiyokosabrey:20180812144305j:plain

再生してみます。

f:id:hiyokosabrey:20180812143954j:plain

成功です。

ファンクションキーの F8 を押して、それぞれのオブジェクトのポジションを確認してみると計算が間違ってないかどうか確認できます。

 

 

配置が完成したみたいだけど。

問題なく並ぶことが分かったので、もう少し数を増やして、あと上面にもう一枚板ポリを載せたりしてできたのがこれ。

f:id:hiyokosabrey:20180812144848j:plain

9x9=81個 並んでます。

上手に「ぷ」→「ち」の順に繰り返し踏んでいくアクションゲームに、なったらいいな。

f:id:hiyokosabrey:20180812145710j:plain

 

というわけで、今回はこれまで。

ではでは

ステキなセンタリングライフを!

 

 

 

 

 

 

テキストのフェード演出を作ってみる

OCTOPATH TRAVELER遊んでます。面白いですよ。「オクトパス」とカタカナで書くと「octopus」だと間違われるかもしれないので、あえてアルファベットで書きます。ということを語りたくなるくらいオススメしたくなるRPGです。

ゲーム中に挿入されるイントロ表現をUMGで作ってみたくなったので試してみることにしまた。4.20もインストールできたし。

 

構造としては、

1行分のWidgetを用意。フェードのアニメーション付き。

文字列を行ごとに分解して行ごとのWidgetに託してフェードを管理するWidgetを作る。

以上の2つでできそう。

フェードは、アルファチャンネルマスクをUVスクロールしようか迷ったけど、結局NoTextureでやってみることにしました。

 

 マテリアル

フェードのキモとなる部分です。テクスチャは使ってません。

f:id:hiyokosabrey:20180721193338p:plain

 左端のScalarParameterノードの値を -1.0 ~ 1.0 にするとグラデーションが動きます。

f:id:hiyokosabrey:20180721203248p:plain

左から白い部分が増えてきて最後は真っ白になります。1-x(OneMinus)ノードで左右の向きを逆にしています。Clampは 0.0 ~ 1.0 の範囲を越えた値をカットして揃えます。

マテリアルは完成です。

f:id:hiyokosabrey:20180721223655p:plain

 

1行分のWidget

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

f:id:hiyokosabrey:20180721195041p:plain

Is Variable と Size to Content にチェックを付けて、用意したマテリアルをフォントマテリアルとしてセットします。

f:id:hiyokosabrey:20180721195458p:plain

 

次にブループリント

f:id:hiyokosabrey:20180721215427p:plain

変数2つを初期化して、テキストブロックにセットしたマテリアルを、ダイナミック化します。このノードをつなぐと、テキストブロックにセットしてあったマテリアルが、マテリアル インスタンス ダイナミック に差し替わります。

マテリアルは通常スタティックなものとして扱われますが、ブループリントから動的にアクセスする場合は、ダイナミックな状態にする必要があるのです。

 

問題発生の話

はじめ、この初期化処理を Event Pre Construct につないでいました。

 Event Pre Construct はコンパイルした際に、実行されるイベントです。

(Add to Viewport  した際も処理は走ります)なので、コンパイルした瞬間にフォントマテリアルは差し替わってしまうのです。

f:id:hiyokosabrey:20180721205046p:plain

これに気づかずに進めていて、ふと Font サイズを調整してコンパイルし、確認のため再生してみると、なんと、すべてのテキストが一斉にフェードするではありませんか。

一旦下図の状態でコンパイルしたあと、

f:id:hiyokosabrey:20180721210612p:plain

再び 戻してコンパイルすると。

f:id:hiyokosabrey:20180721210911p:plain

Before の状態に戻れば 問題ないのですが、Appearance の Fontセクションをいじってコンパイルしていると、最初にセットしたマテリアルに戻らなくなります

f:id:hiyokosabrey:20180721211307p:plain

まだ全部を作ってないので、確認するのは難しいですが、仕様のようなバグのような挙動だったので、一応書いておくことにしました。

 

とりあえず

Event Pre Construct につなぐのはやめました。

カスタムイベントを用意していきます。まずテキストを受け取るイベント。

f:id:hiyokosabrey:20180721212334p:plain

つぎに、フェードを開始するイベント。

f:id:hiyokosabrey:20180721212454p:plain

処理自体はTickで動かすのでフラグを立てるだけです。

で、Event Tick このブーリアンの値をチェックします。

f:id:hiyokosabrey:20180721213330p:plain

文章の長い短いに関係なくフェードの時間を均一にしてしまうと、見た目にとてもテンポが不自然になるので、テキストブロックの長さを元に速度調整用の値を作ってDeltaTimeに掛け算することでブーストすることにしました。短い文章ほど、値の変化が大きくなって短時間にフェードが完了するしくみです。

右の続きの部分はこちら。

f:id:hiyokosabrey:20180721215408p:plain

 フェードの最終値は -1.0 なので、それより大きい状態のときは、素直にフェードの値を更新。-1.0よりも小さくなったら終了なので、Tickの処理を止めるために最優先でフラグを折ります。そして-1.0以下の行き過ぎた値をなくすために、きっちり-1.0にします。

右の端は、イベントディスパッチャーです。

これを用意して呼び出してやることで、自身のフェード完了を親のWidgetに通知します。

f:id:hiyokosabrey:20180721215546p:plain

これで完成。

f:id:hiyokosabrey:20180721223633p:plain

 

全てを表示するWidget

キャンバスの中央にテキストを格納するVerticalBoxパネルを配置します。

f:id:hiyokosabrey:20180721223857p:plain

どんな行数でも上下方向にセンタリングさせたいので、アンカーは

f:id:hiyokosabrey:20180721224445p:plain

ひとまず幅は1280になるようにします。

f:id:hiyokosabrey:20180721225001p:plain

お試しならVerticalBoxがあればOKですが、見た目に雰囲気を出したいので、画面全体の下敷きとフェードインのアニメーションもつくりました。

f:id:hiyokosabrey:20180721225601g:plain

 

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

カスタムイベントを追加して、テキストを受け取って行ごとにバラす処理です。

f:id:hiyokosabrey:20180721231407p:plain

分解判定用文字列の『デリミタ』には 改行を示す 文字がひとつ入っています。

改行文字は Shift + Enter で入力できます。

配列に入れたら、その数ぶんループ処理を回します。

f:id:hiyokosabrey:20180721232212p:plain

 CreateWidget した結果をVerticalBoxの子供に追加。表示するテキストを渡します。

全ての行(Widget)を追加し終えたら、ForEachLoopノード Completeピンから伸ばして画面全体のフェードイン開始です。

f:id:hiyokosabrey:20180721232824p:plain

 フェードインのアニメーションが再生し終えてから、行ごとにフェードを開始するので、アニメーションの終了を待つ必要があります。そのためのノードが、Get End TimeSet Timer by Event ノードのコンビネーションです。

Set Timer by Event を使わずに アニメーションの Event Track を使う方法でも大丈夫ですが、個人的にSet Timer ~ を使う形のほうが、あとからブループリントを見たときに流れが追いやすいのでおススメです。 アニメの尺が変わっても自動で対応してくれます。

Event Track の方は尺が変わってもついてこないのと、呼び出す関数やイベントの名前を文字列で入力するのでタイプミスできないのが気を遣うのであまり好きになれません。このイベントいつ誰が呼んでるの?となった時も調べにくいです。

 

つぎに各行のフェード開始処理です。新しくカスタムイベントを追加して、Set Timer by Event ノードのEventピンとつなぎます。

f:id:hiyokosabrey:20180721235117p:plain

 VerticalBoxパネルに追加したWidgetはIndex番号で管理されていて、番号を指定して取り出すことができます。取り出したままでは使えないので、キャスト(型変換)してやります。追加したときのWidgetがその変換する型になります。

f:id:hiyokosabrey:20180721235709p:plain

「さぁフェードするのじゃ!」と関数を呼び出したあとで、「終わったら連絡するように」というのが右端のバインドノードです。イベントディスパッチャーを作っておけば、bind で検索すると出てきます。

で、終了の連絡を受けたら次の処理。

f:id:hiyokosabrey:20180722000622p:plain

順番にフェードするためのカウント用の変数を一つ加算して、次に進めるかどうかの判定をしています。まだ次があったら、アンバインド Unbind して、最初のイベントを呼びます。アンバインドは、終了通知が1回でいいのと、一度通知を受ければ連絡待機の処理が要らなくなるのが理由です。

f:id:hiyokosabrey:20180722002050p:plain

 

基本部分はこれでできたので、オマケの部分を作ります。

f:id:hiyokosabrey:20180722002014p:plain

せっかちな人向けの処理です。ゲームにも入ってました。

Wd_Sentence のGetノードは、キャストしたタイミングのやつが入ってるので、常にフェード中のWidget が格納されています。

 

次に、テキスト全部を消してVerticalBoxをキレイにするイベントです。

ただ消すだけだとかっこ悪いので、フェードアニメーションを付けます。

VerticalBoxパネルのビヘイビアにRender Opacity というとても嬉しいパラメータがあります。

f:id:hiyokosabrey:20180722002824p:plain

これを 1.0 から 0.0 に下げると簡単にフェードアニメーションが実装できます。

子供として追加した個々のWidgetにフェードアウトを用意しなくてもいいのでとても便利です。

このフェードアウトのアニメーションを作ったら、カスタムイベントで再生するようにします。

f:id:hiyokosabrey:20180722003346p:plain

再生が終わって画面から見た目に消えたら、真の消去を行います。Clear Children という物騒なノードが用意されています。

 

最後に変数リストを載せておきます。

f:id:hiyokosabrey:20180722003939p:plain

完成です。

f:id:hiyokosabrey:20180722004032p:plain

 

再生して確認

テスト再生なので、いつものように便利なレベルブループリントから行います。

レベルブループリントは以下。

f:id:hiyokosabrey:20180722004348p:plain

空行には半角のスペースが入れてあります。

 

では、いざ再生。

スペースキーで表示開始、[B]キーでフェード終了、 [N]キーで全消し。

 

f:id:hiyokosabrey:20180722005129j:plain

f:id:hiyokosabrey:20180722005142j:plain

f:id:hiyokosabrey:20180722005155j:plain

なんとか形になりました。

 

ブログに動画を直接貼れないので、あとでTwitterの方にアップしてみます。

(解像度が心配だけど・・・)

 

ということで今回はここまでです。

 

OCTOPATH TRAVELERUE4で開発されているので、UE4で真似できるはず。そう思いながらUIを触ってます。今のところ、「え?これどうやってるの?」というのは見つけてないですが、そのうち出会ったら、また目コピーに挑んでみるかもしれないです。

 

ではでは

すてきなテキスト演出ライフを!

 

UMGでコンパス表示を作ってみる

ふと気がつくと最後の更新から1ケ月が過ぎようとしててびっくりです。近所ではセミが鳴き始めました。ノートPCのファンに問題があるようでUE4を起動すると3秒と触れないくらい熱くなるため、ミニUSBファン(コンセント接続)と扇風機で冷やすのですが、マウスを握る腕の方が冷えすぎて辛い今日この頃です。

さてさて、今回はシーズン5も大好評の  Fortnite からネタをもらうことにしました。

f:id:hiyokosabrey:20180712230538j:plain

画面上部の方向計をUMGで作ってみます。UE4で作ってるのでUMGに違いない。はず。

f:id:hiyokosabrey:20180712230653j:plain

スクショを取って解析しました。1920x1080で取って、Photoshopでフォントと目盛りを合わせてみて、まずは N ~ S まで(360度分)を横幅2048pxの画像に並べてみた。

f:id:hiyokosabrey:20180712231757p:plain

コンパスは方向を指すので、ぐるぐるとループするようにWrap(ラップ:テクスチャが繰り返される)するはず。

残りの空白をみてピンときました。ループするので、重複部分が入りそう。

 

f:id:hiyokosabrey:20180713233822p:plain

 

そこは画面の表示サイズをみて確信しました。表示幅もだいたい 512pxの様子。

端がアルファで消えてるので確定はできませんけどね。

目盛りも360度分 でちょうど 1536px、 ということは 75% で残りの 25% は 512px と実にキリがいい。

そこでテクスチャサイズを、横 2048px、表示幅512px じゃないかと想定。

 

で、できたのがこれ。2048x32px

f:id:hiyokosabrey:20180712232241p:plain

左端から512pxの幅を表示した際に 0度(北)が真ん中に来るようにしています。

Nが左端から256pxの位置です。

ちなみにフォントは Yu Gothic UI (游ゴシック)にしたらピッタリな感じです。

 

あと、両端がフェードして消えているので、マスクテクスチャも用意します。

f:id:hiyokosabrey:20180712232803p:plain

 

これをUE4にインポートします。

UE4のプロジェクトはサードパーソンのテンプレで進めます。

 

2のべき乗でテクスチャ作ると自動でMipMap作られる設定になっているので、必ず、NoMipmapsにします。

 

マテリアルはシンプルです。横方向にスクロールするだけです。

f:id:hiyokosabrey:20180712233722p:plain

マスクテクスチャは、グレースケールで作っているので、Rチャンネルのピンから取り出して数字テクスチャのアルファチャンネルと乗算しています。

 

表示する大きさは横が512pxなので、TexCoord ノードの中の設定を変更します。

f:id:hiyokosabrey:20180712233922p:plain

 

このマテリアルを、UMGのImageパーツにセットします。

f:id:hiyokosabrey:20180712234526p:plain

ここまでは順調でしたが、ふと、どんな値を受け取るのか気になったので、チェック用のTextBlockを置くことにします。上部の◆は画面中央を示す目印。

f:id:hiyokosabrey:20180712235041p:plain

 編集モードをグラフに移して、カスタムイベントを用意します。

f:id:hiyokosabrey:20180712235359p:plain

受け取ったRotateをBreake Rotator で各軸毎にバラして、Z軸の回転値を表示するように チェック用のTextBlockに対して Set Text します。

Widgetの準備はできました。

 

つぎにThirdPersonCharacterのブループリントを開きます。

マネキン君を選択した状態で、

f:id:hiyokosabrey:20180713234916j:plain

ワールドアウトライナからハイライトされてるやつをチェック。

右端の Edit ~ をクリック。

f:id:hiyokosabrey:20180713235114p:plain

方向を変更している関連のイベントを探します。 

 

 キー入力を受けてカメラを回しているので、それっぽいやつ・・・

Movement inputのコメントを発見。Get Controll Rotaton ノード、そこからZ軸の値だけを取り出してる。

f:id:hiyokosabrey:20180713235501p:plain

よし、この値を確認してみようということで、PrintStringノードでチェックするとどうやら当たりのようです。

Widgetにこの値を流せばいけそうです。

 

HUDクラスのブループリント

ActorクラスのブループリントからHUD表示のWidgetに値を渡す場合、今回はHUDクラスのブループリントを作って対応します。

f:id:hiyokosabrey:20180719002358p:plain

HUDクラスのブループリントを作ったら、

ContentThirdPersonBPBlueprints の中にある

ThirdPersonGameMode を編集してセットします。

f:id:hiyokosabrey:20180719001950p:plain

f:id:hiyokosabrey:20180719001632p:plain

 

これでHUDクラスBPにアクセスが可能になります。

 

作ったHUDクラスBPを編集して、Widgetを表示するようにします。

さらにカスタムイベントを用意して、マネキン君からの呼び出しに応えます。

f:id:hiyokosabrey:20180714001710p:plain

HUDクラスBPの編集は以上です。

 

マネキン君のBPに戻ります。

ここで、HUDクラスBPを経由してコンパスのWidgetに値を送ります。

まず Event BeginPlayを取り出して、HUDクラスBPを変数化しておきます。キャストノードからPromote to Variable すると簡単です。

f:id:hiyokosabrey:20180714002541p:plain

あとは、Movement input 処理の最後にHUDクラスBPに用意したカスタムイベントをつないで値を渡します。

f:id:hiyokosabrey:20180714002938p:plain

ここで確認してみます。

f:id:hiyokosabrey:20180714072733j:plainf:id:hiyokosabrey:20180714072743j:plain

f:id:hiyokosabrey:20180714072759j:plainf:id:hiyokosabrey:20180714072808j:plain

マウスでカメラを回すと、0~360の範囲で値が変化しています。影の向きがひねくれていなければ南中で180°です。

方位 - Wikipedia で調べたら、360°式というやつですね。

 

これをテクスチャのUVオフセット値にしてやればよさそうです。

 

f:id:hiyokosabrey:20180714073520p:plain

 

受け取った値を 360で割ると 0.0 ~1.0 の値にできます。

UVの移動範囲は 0.0 ~ 0.75 なので、 Lerp ノードで線形補間します。

 

f:id:hiyokosabrey:20180714073854p:plain

これで確認します。

f:id:hiyokosabrey:20180714074421j:plainf:id:hiyokosabrey:20180714074429j:plain

うまくいってます。完成です。

 

f:id:hiyokosabrey:20180714080533g:plain

 

テクスチャのUVスクロールで表現してみましたがいかがでしょうか。

実際には 特定の対象の方角を示すマーカーも一緒に動いていることがあるので、テクスチャを使わない方法かもしれません。

 

 HUDクラスのブループリントを使うあたりはちょっと面倒かもしれないですが、GameModeがセットされていればレベル遷移しても安定してアクセスできるし、HUD周りの管理も集約できるので良いと思うのですが、他にもっといい方法やオススメなどありましたら、コメントやTwitterで教えていただけると嬉しいです。

 

 アクションゲームのHUDなので、なるべく負荷がかからないつくりにする必要があります。GPUの描画の負荷とCPUの負荷、それぞれどこまで許容するかはじっくり検討することになります。

 HUDで表示されているものは、ひとつひとつは小さくてすぐ作れそうに思われがちですが、ゲーム中のUI表示ほど難しいものはないと思います。世の中のアプリUI界隈では知見が共有されることも多くて楽しそうですが、あまりアクションゲームのHUDに関しては情報交換が活発じゃない気がします。 あくまでも想像ですが、HUDはデザインよりも機能性が重要視されるので、開発時はゲームプランナーやプログラマが主体となって実装されていくことが多いのがその理由かと思います。だいたいの仕様が見えてきてそろそろ体裁を整えてレビューしようとなった段階でデザイナーが呼ばれるのかなと。

 

 というわけでみなさん、どんどん目コピーしましょう!

UE4はブループリントの触り方が分かってくれば、いろんなことが試せます。

いろいろ試して経験値が溜まってくればモックアップが自作できるようになります。それが動く指定書になって、プレゼンパワーが一気に上がります。うまくいけば実装に携われるようになります。そうなるとどんどんUIをアレンジしたり表現をリッチにできるかもしれません。

 

ではでは

今回はこの辺で

ステキなHUDライフを!