みつまめ杏仁

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

スライドするトグルスイッチを作る

 久しぶりの更新です。まだそんなに秋を堪能してないのに年賀状が売られていたり、おせちの予約受付とか始まってて、年末感が漂ってくるのが切ない・・・梨食べたくなってきた。

 さてさて、ネタを探す旅に出てました~てへぺろ、とか言えたらまだ良かったんだけど、実際は毎日帰るのが遅く、仕事で睡魔と戦うのに精一杯で記事を書くエナジーが無かったのです~てへぺろ

 今回は、トグルスイッチを作ります。ゲームのオプション設定とかでよくあるUIで、ON か OFF かの2つの状態を切り替えるスイッチです。チェックボックスも同じ機能を持っているんだけど、アニメーションさせた方がリッチに見えるので、スライドするタイプをマテリアルで表現します。値の管理方法として他の設定項目のことも考えてストラクチャを使ったりしています。また、イベントディスパッチャーやバインドは使いません。最後の方にUI制作のワークフローについて少しだけ触れています。

 

f:id:hiyokosabrey:20181104234530j:plain

クリックするたびに切り替わります。

f:id:hiyokosabrey:20181104235057g:plain

UV移動なので、スライドをやめればいつでもチェックボックスに転用できます。

 

まずテクスチャを用意します。

f:id:hiyokosabrey:20181110130710p:plain

 

Sizeは 256x64 で RGBはこんな感じ。

f:id:hiyokosabrey:20181105232325p:plain

フリンジが気になる場合は黒い部分をなくしてカラーを入れます。

アルファチャンネルは片側だけを白くして抜きます。

f:id:hiyokosabrey:20181105232353p:plain

 

これをマテリアルで制御します。

f:id:hiyokosabrey:20181110130810p:plain

 

f:id:hiyokosabrey:20181105233938p:plain

テクスチャの一部分を切り出すには、TexCoord ノードのタイリングを利用します。

縦方向(V)は全部使うので 1.0 のまま。横方向(U)は以下のように計算します。

f:id:hiyokosabrey:20181105235722p:plain

切り出したい大きさを、テクスチャのサイズで割ると求められます。

f:id:hiyokosabrey:20181106000103p:plain

 

もっと大きなテクスチャでも基本的に計算方法は同じ。

ただし下図のように左上に配置していない場合はオフセットの値が必要になります。

f:id:hiyokosabrey:20181106231325p:plain

 

 UV値は0~1の値なので、小数を扱うことになります。テクスチャサイズで割り算することになるので、配置場所や面積(パーツのサイズ)は極力偶数にすることをオススメします。奇数だと割り切れないため誤差が生まれやすく一部のピクセルがキレイに描画されなくなったりします。テクスチャ圧縮がかかる場合は、さらに4で割り切れる場所にパーツを配置することをオススメします。後からテクスチャの解像度を変える場合でも偶数にしておけば安心です。

 UI表示はカメラに依存せず静止していることがほとんどなので、ピクセルが荒れていると目立ちます。面倒ですがこのあたりは丁寧に扱うに越したことはないです。

 

切り出すUVの範囲が分かったところで、今度はUVを動かす範囲を計算します。

f:id:hiyokosabrey:20181106233013p:plain

96をテクスチャサイズで割った値が移動量です。テクスチャサイズが 256pxだと、0.375 になります。

 

この移動をUMGのアニメーションで動かします。

f:id:hiyokosabrey:20181110130919p:plain

 

まずはTextBlockと共にキャンバスに配置します。

f:id:hiyokosabrey:20181107010948p:plain

トグルスイッチのテクスチャは、画像ではなくマテリアルをセットします。

f:id:hiyokosabrey:20181107213120p:plain

 

TextBlockには is Variable のチェックを付けておきます。

 

 

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

Imageパーツの Pivot の値を 0.0 ~ 0.375 でキーを打ちます。

f:id:hiyokosabrey:20181107212722p:plain

UMGはここまでです。

 

エディタウィンドウをGraphに移動してブループリントを編集していきます。

f:id:hiyokosabrey:20181107213947p:plain

 常時走る EventTick を使っていますが、トグルスイッチなどのUIパーツはセッティング画面で使うことが多いので、ポーズ中とかゲームプレイ以外だと、それほど問題にならない、はず。Pivotの値を取り出して、毎フレームマテリアルのパラメータに渡しています。

 少しでも処理をシンプルにしたいので、コストの高いGet Dynamic MaterialノードはTickに入れないようにして、Event Construct で最初に変数化しておいたものを利用します。

 次に、トグルスイッチを動かすイベント。

f:id:hiyokosabrey:20181107222540p:plain

 UIのインタラクションはユーザーの操作で割り込まれるのが常なので、割り込まれても問題なく動くようにします。 なるべくアニメーションの尺を短く作るのも大事。

 

 最後に、初期値をもらって反映する関数。

f:id:hiyokosabrey:20181107223147p:plain

トグルスイッチのWidgetは完成です。

 

次は管理とレイアウトの仕組みを作っていきます。

管理はプログラマでなくても扱える ストラクチャ (構造体)ってやつを使ってみます。

f:id:hiyokosabrey:20181110131023p:plain

コンテンツブラウザから作成してエディットします。

f:id:hiyokosabrey:20181107223709p:plain

 

New Variable ボタンをクリックして名前との型をセットします。

f:id:hiyokosabrey:20181107224428p:plain

Saveして閉じます。

 

新しくレイアウト用のWidgetブループリントを用意します。

f:id:hiyokosabrey:20181110131119p:plain

 

キャンバスに用意したトグルスイッチのWidgetを並べます。

f:id:hiyokosabrey:20181107224827p:plain

 

Graph エディットに移行して、

変数を一つ用意します。

Variable Type を 先ほど作ったストラクチャ にします。

f:id:hiyokosabrey:20181107225412p:plain

 このストラクチャ型の変数をグラフに取り出したら、Breakノード経由で中の値にアクセスできます。今回はブーリアンばっかりですが、いろんな型をひとまとめに扱うこともできます。

 値を取り出してキャンバスに並べたトグルスイッチのWidgetに渡します。

f:id:hiyokosabrey:20181107225858p:plain

これで初期値とラベル名がセットされます。

ここで一旦画面で確認してみます。

このWidgetを保存してレベルブループリントから表示させます。

f:id:hiyokosabrey:20181107231302p:plain

 右の赤いブーリアン型のノードは、 エンジンに最初から用意されているパラメーター設定用のノードで、PlayerController から取り出します。

再生するとこんな感じ。項目の名前とストラクチャの初期値が反映できています。

f:id:hiyokosabrey:20181107231724j:plain

 

仕上げにマウスイベント。

クリックしてスイッチを切り替える処理を用意します。

GraphエディタのFunctions 欄 にある Override ボタンを押して、

On Mouse Button Down というのを選択。

f:id:hiyokosabrey:20181107232718p:plain

特殊な関数を編集できるようになります。

 

あらかじめ引数と戻り値のピンが設定されていて変更できない作りです。

f:id:hiyokosabrey:20181107233014p:plain

Warningが出ているので、黙らせるには、Unhandledノードをつないで・・・

f:id:hiyokosabrey:20181107233449p:plain

コンパイルすると鎮まります。

この関数は、自身のWidgetの上で、マウスのクリックが検出されたら呼び出されます。

白いラインの間にPrint Stringノードを入れて再生してみると分かります。高速でクリックしても反応しないのは、ダブルクリックと区別するためで、ダブルクリックはまた別の関数で処理することができます。

ここに、トグルスイッチのWidgetをクリックしているかどうかの判定と、スイッチの切り替えの処理を並べます。

今回スイッチが4つあって、全体はこんな感じです。

f:id:hiyokosabrey:20181107234617j:plain

マウスクリック系のイベントはひとまとめにできます。

 

基本的に、クリックした瞬間、カーソルは誰の上に乗っていたのか?をチェック。

f:id:hiyokosabrey:20181107235123p:plain

カーソルが乗っていたらどうするか? こうします。

f:id:hiyokosabrey:20181107235718p:plain

ストラクチャ型の変数から取り出した値をひっくり返して入れ直します。

Not ノードを使うと、ブール値は反転します。

 

これで完成です。

f:id:hiyokosabrey:20181108002615g:plain

 

 今回はプログラマとデザイナのブループリント編集がなるべくぶつからないように安全に触れないかな、と考えながら試作しました。

基本的に

 デザイナは見た目を担当。受け取った値を反映するとこまで。

 プログラマは並んだ各パーツを管理してシステムの値と連携するところ。

 

今回のアセットをそれぞれで担当を分けるとしたらこんな感じです。

f:id:hiyokosabrey:20181110125426p:plain

 この辺を考慮してイベントディスパッチャーを使わない仕組みです。

 UIパーツの方にマウスイベントの検出を入れると、イベントディスパッチャーを Call する処理と、管理側のWidgetBPからバインドが必要で、そこそこ連携しないといけないので、メンテナンスや調整のとき編集がバッティングしやすかったり、デザイナとプログラマとのコミュニケーションコストがそれなりにかかるのが予想されます。

 あと、マウスイベント処理をある程度まとめてON・OFFしたり管理できないと、後から条件がややこしくなるので、それを避ける狙いもあります。

 

 UIの見た目は自由にUIパーツの方で受け持って、設定画面を抜けるときに、UIパーツから最終の値を全て回収するという方法も考えられますが、UIパーツの状態を見てリアルタイムに反映させたい場合(サウンドのボリュームとか)は難しいし、値の回収を失敗する可能性を考えるとかえって手間がかかりそうです。

 他にも効率的で安全な作り方がありそうですが、いったんこんな感じでいかがでしょうか?

 

 トグルスイッチUIがたまたまいい感じになっただけだと思うので、もうしばらく検証していこうと思います。今までゲームパッドとかのコントローラ操作をメインに考えてきたので、マウスやタッチ操作にも馴染んでいかないとね。マルチプラットフォーム対応はUI泣かせですし。

 

ではでは

ステキなトグルスイッチライフを!

 

f:id:hiyokosabrey:20181110133023p:plain

 

 

 

 

 

 

 

 

 

ぷちコン応募作品のプロジェクト公開について補足

 台風24号がこちらに向かっている中記事を書いていますが、また停電は嫌なので遅筆ながら急いで書きます。

 UE4ぷちコン第10回、残念ながら選に漏れてしまいました。サクッと作ってみてそこからネタ拾っていけたら記事にしよう、と考えていたのでそこはまぁまぁ達成できたのではないかと。結果発表後の動きで応募作品を公開されている方がいらっしゃるのを見て、私もソワソワしてきて公開することにしました。Twitterにはすでに呟いていたりします。

f:id:hiyokosabrey:20180930093128p:plain

Puchi_MMAn_nin.7z - Google ドライブ

ファイルサイズは約44.5MB

UE4のVerは 4.20

 

作品内容についてはこちら↓

www.youtube.com

 

公開することについて

 結果発表で触れられてもいないのに公開って、どんだけ自信ありまくり?という声が聞こえてきそうですが、内容についていくつかこのブログに載せているのと、その記事を読んで興味を持ってもらえたなら動くサンプルとして見ていただたらいいなと考えて、プロジェクトごと公開するに至った次第です。

 なるべく見やすくノードを整理しているつもりですが、ごちゃついていたり、もっと最適化できそうな部分などが結構残ってます。ブループリントのようなビジュアルスクリプティングでは、テキストベースの共有ほど手軽じゃないので、ノード構成や最適化の研究って、直接触りながらの方がいいのかなと思ってたりします。

 あくまでもブログ記事と連動したサンプルとして公開してます。UE4での動作確認は行っていますが、プロジェクトデータによる問題等が生じた場合は、Epicではなく当ブログへコメントいただくか、Twitterアカウント @MMAn_nin の方へご連絡いただけると対応します。

 

 わりと簡単にプロジェクトを切り出して「ほい」って上げられるのは嬉しいのですが、いくつか気になったことがあって、急ぎ上げ直すことになりました。その理由としては以下。

 

ソースパスが見えちゃう

 エンジンにインポートする素材は修正したらすぐに手軽に再インポートできるので、こまごまとブラッシュアップするには大変手軽で嬉しい機能ですが、参照している場所の情報「ソースパス」を晒すことになります。

 特別に意識して対策していないと、Windowsのログオン名がファイルのパスに含まれてたりするので、うっかり、というのがあります。家だと趣味丸出しの厨二ワードだったり会社だと何かしらの管理IDだったりするかもです。

 この辺、外に切り出す際に、受け渡しに不要なフォルダ(Developersとか、IntermediateとかStarterContent)をフィルタリングしたり、ソースパスを偽装するかNullにするようなツールか何かがあるとありがたいと思うのですが、どこかにあるのかな?。 

 余談ですが、Unityはエディタ内に再インポートの仕組みがないのは、この辺の情報を抱えたくなかったからなのか、という気がしてる。

 

 

再配布可能かどうかの確認が必要

 プロジェクトデータに限った話ではないですが、外部のフリー素材を使っているコンテンツを公開する場合、あらかじめ利用規約はしっかり理解しておく必要があります。提供元によっては許諾範囲に制限があることがあります。 製品だと、クレジット画面などで表記する機会が用意できますが、プロジェクトデータの状態でとなると、触れる機会が無いので、ひとまずといった対応ですが、著作権表示および提供元サイトへの誘導としてサイト名とURLを書いたテキストファイルを一緒にアップしました。ファイルの場所は プロジェクトフォルダの直下です。

 確実に目に留まる場所ではないので、その対策のひとつとしてこの記事を書いています。

 今回の公開データで素材が気に入った場合は、ぜひぜひステキな素材提供サイトをご利用ください。

 

《BGM》

musmus.main.jp

 

《SE》

soundeffect-lab.info

 

 

作品について

 今回ゲームとしてワクワクするところのないアイデアだったので、次回はもっと盛り上がれる要素を入れていけたらいいなと思ったりしています。ちゃんと動くものを作ろうという点ではそれなりにカタチにできたので満足しています。

 ボリューム的にいろんなものを短期間で用意しないといけないので、サクッと作れるようにもっと使いこなさなければ、と決意を新たにしました。とはいえ範囲が広すぎるので、気になるところから地道に触っていくしかできませんけどね。参加できる限り参加していって、提供できそうなネタがあれば記事にしていこうと思います。

 

 おっと、ずいぶん台風が迫ってきました。今回も影響が大きそうなので、身の安全を第一に過ぎ去るのを待つことにします。明日みなさまが青空を無事に迎えられますように。

 

私は月曜の朝から胃カメラの予約があるので憂鬱ですw

 

ではでは

ステキなUE4ライフを!

 

 

 

 

 

 

 

 

 

 

 

 

 

HorizontalBoxの中の表示間隔を調整する

 今回応募したぷちコン作品では、プレイヤーを操作して、浮いているタイルを踏んだ数をカウントしてスコアに加算しています。リザルト画面で表示する際に、ただ数字を書くだけだと面白くないし、「いっぱい踏んだなぁ」「あまり踏めてないなぁ」という実感を噛みしめるためには、ビジュアルで示すのが効果的です。このとき踏む数が想定よりも多かった場合にスキマを詰める処理を実装しました。今回の記事は事例紹介的な感じで書いていきます。

f:id:hiyokosabrey:20180922163532j:plain

10個までは一定の間隔で並べて↑

10個以上になると数に応じて詰めます↓

f:id:hiyokosabrey:20180922163603j:plain

 細かい仕様ですが、0個の時は半透明にしてます。

 ちなみにこの文字の種類による点数差は「屈辱点」てやつで、昔のアーケードゲームでよく見かけた仕様を取り入れてみました。点数獲得の単位が100点刻みだと、普通にプレイすると10の位まで0が並びます。ところがランキング画面(今だとLearderboardsっていう方が通るかな)をよく見ると、1の位に数字が入っていたりします。これは「コンティニューしてる・・・金の力でクリアしやがった」、百以下が 00だと、「お!ノーコンクリア!すげぇ!」となるやつです。コンティニューすると1点もらえる!って素直に喜んじゃいけないのです。

 実はランキング画面も作るつもりだったのですが、ステージのタイルがランダムだとスコアを競う気がなくなるのでやめました。高得点のルートが決まるのも面白くないのでランダムは残すことにした結果、ただの運ゲーになってますw

 

 ちょっと脱線してしまいましたが、Widgetの中身について書いていきます。

キャンバスにはキャラを並べる HorizontalBox と スコア用の TextBlock を並べています。

f:id:hiyokosabrey:20180922181356p:plain

テクスチャは 64x64が4枚。

f:id:hiyokosabrey:20180922212735p:plain

 

これらを、配列に収納してしまいます。

f:id:hiyokosabrey:20180922182442p:plain

 配列に入れておくと、Index番号で扱えるようになるので、ある程度機械的に処理できて便利です。後から要素を追加しても同じように処理できます。また、アナログ的に並んでいるものでも順序を決めて管理できるのも魅力です。

 

 上の図では、Add to Viewport するたびに走るので、Do Once ノードを入れています。リザルト画面が終わったら、Remove from Parent していますが、描画されなくてもメモリには残すようにしているためです。

 この配列たちは一度作ったら、中身が変化せず、繰り返し使用するタイプなので破棄する必要がないのも理由のひとつです。

 

配列の準備ができたので、次はスコアをセットしていく部分です。

別の関数ですでに「ぷ」「ぶ」「ち」「さ」のそれぞれの個数を受け取っています。

それを↓この配列に保持してあります。

f:id:hiyokosabrey:20180922190748p:plain

この配列の中身を ForEachLoop ノードで取り出しながら処理していきます。

f:id:hiyokosabrey:20180922202631p:plain

真ん中付近にいる Pure型の関数 が表示間隔を計算する関数です。

f:id:hiyokosabrey:20180922194153p:plain

並べる文字のテクスチャサイズは 64x64です。 それが10個で640という幅が、表示限界幅とします。

f:id:hiyokosabrey:20180922195349p:plain

まず 引数(Inputs)で受け取った個数で、表示限界幅 640 を割ります。

640 ÷ 個数 で1個あたりの幅が計算できます。そこから 64 を引いた値が、基本の表示間隔 = 64 に対しての補正値となります。

f:id:hiyokosabrey:20180922205801p:plain

 

なぜこんな補正値が必要かというと・・・

例えば、 11個だった場合。

640 ÷ 11 で、58.1818181818・・・ となるので、1個が 58.181818・・・の間隔で並べばいいのです。

ところが、HorizontalBox は追加した 子パーツ の幅をそのまま維持して並べようとします。58.18181818・・・ずつ、といっても聞いてくれません。そこで、SetPadding ノードを使います。

f:id:hiyokosabrey:20180922210214p:plain

 

ここに渡す値を用意するために、もう少し計算が必要です。

 58.1818181818・・・からさらに 64を引くと、

-5.81818181818・・・・ となります。(符号はマイナスになりましたが、小数点が一つ動いただけに見えて面白い結果ですね。)

これで補正値が計算できました。

 

この補正値を HorizontalBox にテクスチャ文字を追加する際にPaddingの値として渡せばいいのです。

その仕事をするマクロがあります。

先の ForEachLoop で処理していたところの、真ん中付近、少し上と右に計2つあります。

f:id:hiyokosabrey:20180922202631p:plain

このマクロの中身はこんなかんじ。

f:id:hiyokosabrey:20180922211025p:plain

HorizontalBox に追加した 子パーツ に対して(正確にはAdd Child ノードの戻り値)、HorizontalBoxSlot にキャスト(型変換)すると、SetPadding ノードがつながります。その In Padding Right に補正値を入れてやると、次に追加された子パーツ がその補正された場所に追加される仕組みです。

ついでに個数が ゼロ だった時の処理も入っています。

 

実際に 8、9、10、11 個の場合の表示はこうなります。

f:id:hiyokosabrey:20180922202052p:plain

 

 状況に合わせて動的に子要素を追加する場合、エディタで事前に予測して調整できればいいのですが、臨機応変な対応が必要となるとブループリントでどうにかすることになります。SetPaddingノードを使うと表示間隔を自在に調整できるので、今回の仕様が実装できました。VerticalBoxでも同様のことができます。

 

ではでは

今回はここまで

ステキな表示間隔ライフを!

 

 

 

 

 

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

 今回のぷちコンで作った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のタイマー作動イベントを呼ぶヤツが必要になるのですが、何がトリガーになるかはゲームの内容次第なので、ひとまず今回はこの辺までにしておくことにします。

 

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

 

ではでは

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