みつまめ杏仁

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

キラキラを作る

Unreal Engine (UE) - Qiita Advent Calendar 2025 - Qiita

アドカレ 19日目に参加させていただきます。

ゲームUIのことで何か、ということで、最近のゲームUI開発について思うところをちょこっと書いて、キラキラしようと思います。

 

 多分察しておられる方もいらっしゃると思いますが、自分はゲームエンジンがまだ無かったころからこの業界にいます。まずデザインがあって、どうにかしてゲームに組み込まなくちゃいけない。このハードウェアで何ができるのかわからない。といろいろ試しながら手探りで開発してました。つまり当時はとてもハードウェアに近いとこにいたわけです。UIだけの特別な作り方や技法などがあるわけもなく。おかげそれなりにグラフィックアーキテクチャにも関心を持つようになりました。

 

 ゲームエンジンが登場して指定書の量が格段に減りました。

 

パーツAが X座標860 から 120まで減速しながら 16フレームで移動します。12フレームまで移動したタイミングでパーツBを透明度 0 から徐々に上げていき、20フレーム目で255にしてください。

 

 みたいなテキストをプログラマに渡していたのが、タイムラインの登場によって、プログラマがいちいち文章を読み込まなくていい形になりました。素晴らしいですね。

しかもタイミングや細かな数値の変更をプログラマの手を止めることなく行えます。

ゲームエンジンを使った開発も、GUIの編集ツールにおいては最初から充実していたということはなかったものの、いまやこれらの開発環境が当たり前になっています。

 

開発がスタジオなんかの組織で開発していると、

毎年 新人がやってきて、研修でいろいろお仕事について、エンジンやツールの使い方など教わります。

先輩「このエディタを起動して仕事をするんだよ」

新人「はい!」

といったやりとり。ありふれた光景ですね。

 

 現場に入ってもしばらくは、失敗しないマニュアルにしたがってタスクを消化するだけの仕事が振られがちです。新人に失敗させれば先輩の指導不足と言われかねないし、まずは成功体験を積ませてくじけにくい心を育てる。これはこれで致し方ないのですが、重要なマインドセットを忘れないようにしたいものです。

 ゲームエンジンを使いこなすことは、組織の一員として、開発効率上げること、これはとても重要だけれど

 

 ゲームという娯楽商品を作っている

 

 このマインドは決して忘れてはいけないと思うんですよ。

 最初から GUI制作の専用ツールがあるのはとても素晴らしいことだけど、専用過ぎて周辺の技術に目が行きにくくなっている気がします。

 専用ツールがあるということはそれを駆使するのが当然であるけれど、そのツールでできないということは、やらないほうがいいんだ。というマインドはちょっと残念です。

 

 世間にあふれているタイトルをよく見てみると、どうやって作ってるのか、ちょっと見ただけではわからないものがたくさんあります。そういったヒントをツールで試してみるのはツールを理解する手助けにもなるし、ツールでできなかったとしてもグラフィックアーキテクチャへの理解も進みます。もっとシンプルに、面白い表現、斬新な見せ方、演出なんかを見つけていけたらなと思います。ツールが扱えることは大事だけど、もっといろんな表現、ゲームグラフィクスにも目を向けてほしいな、という話でした。

 

 というわけで、そろそろUMGの取り扱いに慣れた頃合いだと勝手に想定して簡単なスタティックメッシュを使った何かキラキラしたモノを載せておこうと思います。

 ただの拡縮じゃない頂点アニメーションを頂点カラーで制御しようというものです。

 一見地味ですが、アイデア次第で応用できると思います。

 

用意するものは Blender です。特別なことはしていないので、Mayaの操作に慣れた方でも理解できる範囲だと思います。

 

 

 

テクスチャを作る

まず星を作ります。テクスチャ節約のため点対称で描きます。

シンメトリーであればOK。

いったんスマートオブジェクトに変換しておきます。

 

中央に正方形の半透明レイヤーを重ねて置きます。

ちなみに今回のサイズの目安は 448 x 448で テクスチャは 256 x 64 にしたいので、

赤い四角は 64 x 64 にしました。

 

この四角で選択範囲を取り出して、

イメージ > 切り抜き

で中央部のみにします。

 

続けて
イメージ > カンバスサイズ

で右側を広げます。

スマートオブジェクトにしておくと中身が維持されます。

 

 

このPSDを保存してBlender

 

 

 

メッシュを作る

Blenderは 4.2.1 LTSを使用しました

デフォルトのキューブを消して、パースのつかないプレーンで作業します。

エンジンに持って行ったときに、あまり手数がかからなければなんでもいいと思う。

 

Add > Mesh > Plane で 4頂点の板ポリを追加。

 

こういった分割を作ります。

45°回して、4頂点を伸ばします。

これに用意したテクスチャを貼ります。

 

ピクセルにスナップさせると正確に貼れます。

 

最初の点対称で描いたピクセルがずれていると、下のようになったりするので、

テクスチャを調整します。

 

 

頂点カラーをつける

 まず中央の4頂点を選択します。

 

に切り替えて

ペイントするカラーを 0.5 の グレー にします。

塗のタイプを頂点に切り替えるて、Paint > Set Vertex Colors を選択 

 

に戻って頂点を選んで

で色を付けると繰り返します。

いったん Edit Mode から Vertex Paint にマウスで切り替えると、[ Tab ] キーで行ったり来たりできるので楽ちんです。直前のモードと行き来できる仕様。

 

つける色は

Bはすべて同じ値なので、シェーダーで固定値を入れるのもアリです。

 

ちょっと色味が気になる場合は、ビューポートの設定を変更します

このポップアップの一番下

スペキュラーライティングをOFFにすると、

すこし白っぽくなっていたのがクリアになります。

スペキュラーライトの設定は書き出すデータに影響はないのでお好みで。

 

FBXで書き出す

で モデルを選択した状態で

File > Export >FBX

 

書き出しダイアログには、設定できるオプションがたくさんあります。

余計なものを書き出したくないので、Selected Object にチェックを付けます。

 

作業プレーンによって ここの設定を変更します。

下図は XZプレーンで作成した場合。

 

多分デフォルトから変えなければ大丈夫です。

 

設定はこれくらいで、あとはファイル名を決めて書き出します。

 

 

インポートする

書き出したFBXファイルをインポートすると設定ダイアログが出てきます。

Meshに

コリジョンは使わないのでチェック外す

 

Mesh > Advance に

頂点カラーのオプションは Replace (デフォルトはIgnore)

 

マテリアルはエンジンで作ってセットするので、

さない、ったりない、クスチャ取り込まない

頭文字を取って 「サッシテ」

・・・すみません調子に乗りました。

 

あとは Import ボタンを押せばOK。

 

Blender のバージョンが 4.1以前だと下のような警告ポップアップが出ることがありますが閉じても大丈夫。

 

 

テクスチャもインポートします。

 

ちょっとシャープにしたやつ。

 


マテリアルを作る

今回の頂点カラーは、『移動方向と量を示す』という使い方をします。

「量」は カラーの輝度で表します。 最大値は 1.0 でマイナスの値は無し。

ここで -0.5 することで、0を中心としたプラスマイナスの関係になります。

Z アップ(Z軸のプラスが上向き)なので、青を縦軸にしています。
赤は水平、緑が奥行き方向です。

 

Zアップの状態で頂点カラーを着色したので、インポートした後の食い違いを解決するために GB を入れ替えるようにつないでいます。

途中 2 を掛けているのは、±0.5 を ±1.0 にしているだけなので、そのあとの Multiply で掛ける値を、2倍にして扱えば、無くても大丈夫。

 

頂点の変形をシーンに配置したメッシュに反映させるために、

こちら↓ の記事を参考にさせていただきました。

[UE5]マテリアルでモデルを動かしてみよう|株式会社ヒストリア

 

できた頂点変形の部分

 

カラーとアルファの部分はこんな感じ

テクスチャのタイリング設定は Clampです。

 

 

テストでTimeノードを つないでみると、

こんな動きになります。

これをたくさん表示してみようと思います。

 


アクターを作る

アクタークラスのブループリントを作って、用意したスタティックメッシュとマテリアルをセットします。

 

エディタを開いて、スタティックメッシュのコンポーネントを追加します。

左上の Add ボタンから StaticMesh を選択。


エディタ右にある、Detailsパネルで、 メッシュアセットとマテリアルをセット。

あとは

Cast Shadow ・・・ False

Collision Presets ・・・ NoCollision

を設定変更しておくといいかも。

 

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

作りたい 振る舞い は 3つ

  1. 常にカメラを向く
  2. アニメーションする
  3. 消える

 

 

まずは常にカメラを向かせます。

ここでは Rotation Z をカメラの位置に向けて、それ以外は変化しないようにしています。Event Tick につないで毎フレーム計算します。

 

表示するときに、いくらかサイズにバラつきを持たせたいので、 Float型の変数をひとつ用意します。

外から書き込んで使うので、 Instance EditableExpose on Spawn を有効にしておきます。

 

アニメーションには AddTimelineノードを使います

タイムラインの中身はこんな感じ。

ある程度、星の腕がある程度短くなったら、スケールを小さくして消す感じにしました。

タイムラインの実行が終わったら、Destroy ノードでサヨナラです。

AddTimeLineノードの Finished ピンに Destroy Actorノードをつなぎます。

 

 

プレイヤーの位置に置く

レベルを作成します。New Level で Basic あたりだと軽いです。

ひとまずマネキンが操作できれば大丈夫です。

 

レベルブループリントから一定間隔で、プレイヤーのポジションにアクターを配置します。

プレイヤーが動いていると出現して、止まっていると出さないようにしたいので、最後にスポーンしたロケーションを保持しておくための Vector型の変数を用意します。

比較して差異がなければ何もしません。

 

再生中は常にプレイヤーの位置を取得しつづけます。

Event Tickでもいいのですが、取得タイミングを調整できるようにカスタムイベントにしました。

つづき

数が少ないと寂しいので、2つずつスポーンするようにしています。

 

 

アクターをスポーンさせるのはこのノード

Class のところに、用意しておいた アクターBPをセットします。

セットすると、アクターBPの中の変数に Expose on Spawn を有効にしているので、自動でピンが追加されます。

 

スポーン場所に揺らぎが欲しいので、プレイヤーの位置にいくらかのランダム値を加えています。任意の範囲で乱数を発生させる関数を用意。

スポーンさせるときの Transform に渡すようにします。

最後は Set Timer by Event ノードでカスタムイベントを再呼び出し。これでずっとループします。

 

Event BeginPlay で呼び出しておくだけで、再生時に動くようになります。



再生してみる

youtu.be

 

トゥーンなキラキラエフェクトなので、漫画っぽく立ち絵に乗せたり、目や頭にあしらってみたり、してもいいかもしれない。

 

ちゃんとプールする仕組みを作ったりしたいところだけど、今回はカンタン頂点アニメーションを楽しもう!という謎企画なので、このあたりで許してもらえると助かります。Niagaraを試してもよかったと今になって思ったり。

 

今回のはただの拡縮に見えなくもないけど、ただ四角いテクスチャを画面にレイアウトする以外にも いろいろ作れるようになってほしくて、メッシュを使った UI 表現に馴染むきっかけになってくれたらいいなと願ってます。

 

今年はゲージの記事ばかり書いてましたが、来年は、PCも新しくなったことだし、メッシュを使ったテクニックを書いていけたらと思ってます。

ストレージも大きくなって、UEもようやくアップデートできるようになるしで、検証が捗るといいなぁ。

 

年末進行で慌ただしい時期だったりするかもですが、風邪などひいて貴重な冬休みがなくならないよう 皆様 お体にお気をつけてお過ごしください。

 

ではでは

素敵なクリスマスを!

 

 

qiita.com

 

 

 

 

 

 

 

 

100本ノック振り返り

 何とか100本をカウントすることができてホッとしています。約一年半かかってしまいました。100本という数字のいかに重たいかを身をもって知ることができた。見積もりの甘さゆえの楽観的な数字だったと今更ながら思う。いけるんちゃう?みたいな。

 桜井さんのように事前に内容を精査して原稿を準備していれば、30本ノックになっていたか、もっと早くに終わっていたかもしれない。

 途中本業の繁忙期に突入して時間の余裕がなくなったりしたので、更新間隔が空いてしまったときは、ネタ切れで逃亡説も囁かれていたかもしれませんね。実際ネタ募集したこともありましたし。

 

 書きながら改めて検証したりして、扱い方の印象が変わるノードもありました。経験則で理解したつもりになっていたけど、現場でちょこっと試して解決してしまう程度では、深い解像度での理解に至らないのを痛感しました。

 

とりあえずリニアグラデーションをゲットできればゲージが作れます。

本来 TexCoord ノードは UV座標を扱うノードです。

 

 シェーダーを覚えたての頃は LinearGradientノードを使っていました。名前がズバリなので安心感があったのです。

 このノードは Detailを見るとMaterial Function Call とあるので、UEで用意されたノードです。ダブルクリックすると中身を見ることができます。

これを見たときのインパクト。

え? TexCoordノード、ていうか UVってこんな使い方もできるんだ。

 

LinearGradient ノードは ComponentMaskノードをつながなくていいのは便利なんですが、サンプラーにつなぐときには ひとつにまとめないといけないのと、TexCoordノードは登場頻度が高く、[U] +クリックで取り出せるしで、LinearGradientノードは私のよく使うノード集から消えました。

 

このリニアなグラデーションに対して、Ifノードで分割していたのが Stepノードになり、色のないところに色を付ける Lerpノードと組み合わさってゲージの基礎ができあがりました。

いまのところ この構成でいい具合にゲージ表現できているので、あえて経過を辿るようなことはせず、基本の構成としてビジュアルのアレンジを楽しむという形にしています。

 

 というわけで、シェーダーを使ったビジュアルをいろいろと試しながら、使えそうなやつ、面白そうなやつ、特殊なやつを網羅したら 100本なんて、あっという間・・・のはずだったのですが。以外にできることは少ない。でもちょっとしたアレンジで印象が変わるなら、その辺も採用していけば、反復練習にもなるんじゃないかと割り切ってみたのですがいかがでしたでしょうか。

 

 扱い方に慣れてきたら、次は自分のアイデアを試してもらえたら嬉しいので、紹介したゲージのサンプルは、テストするためのものというニュアンスを込めて時間をかけないでつくれる素材を意識しました。

 

 途中から記事を見始めると、怪しいノードが出てくることに気づくと思います。

 マテリアルパラメータコレクションという仕組みを利用して進めました。

 ゲージは動かないとテストできないので、動かすためにタイマーノードを入れたりすると紹介するときに邪魔です。そこでレベルを再生してマテリアルパラメーターコレクションに対して常に値を書き込んでいれば、大量のマテリアルを作っても簡単に一律のテストができます。初回の記事で解説していますので、興味が湧いた方はぜひ試してみてください。

 

 作ったゲージを画面いっぱいに並べても一斉に動くので壮観です。

youtu.be

イベントで変化するやつは、キー入力に反応するようにしています。

一度こういったテスト用のレベルを作っておくとちょっとしたテストが気軽にできるのでオススメです。

 

 シェーダー内で使うコマンド(命令)はノードという形で順番に組み合わせて最終的な出力結果を想定通りの物にします。同じ結果にたどり着くとしても、いくらか組み合わせに幅があります。「結果オーライ!(汗」が ある程度の「確信」に変わるまでサンプルを作ったりして「よし!」って安心しつつも、しばらくするとすっかり忘れて過去にやったのを思い出しながらつないでいたら、思ってたより少ないノードで作れたり、または調整の仕組みをひらめいたりします。

つまり

 

慣れる

 

これに尽きます。

Photoshopやクリスタなどのレイヤーのブレンドについて計算式を理解してから使う人なんていないんじゃないかと思います。シェーダーも、同じようにノードの組み合わせを試して結果を体験してみるところから始めればいいと思うのです。

 ディゾルブやディストーショングリッチみたいな演出やエフェクトで使えそうな目を引くやつは、難しそうに見えてしくみは簡単。解ってしまえばたいして難しくないけれど、素材がクオリティを左右するので、いい感じの素材ができるまで四苦八苦することも。素材の塩梅が大事ではあるけれど、まずはUV座標をいじるとどうなるのか、加算や乗算、 値(しきいち)を使うと何ができるのか、値を別の用途で使ってみる、というような試行錯誤を経て、ノードの扱い方さえ覚えられたら、あとは工夫と根気でどんどんいいものが作れるようになります。

ということで実用レベルでの習得を後押しするために 100本用意しました。

つまり

 

やってみる

 

これに尽きます。

 

シェーダーを推す理由はいくつかあるけど、特に大事にしたいのは 2つだけです。

テクスチャの効率化 調整コストの削減

これ以外は、タイムラインなどのアニメーション制御のしやすさ、装飾、演出 での活躍が期待できるので無理しない程度にチャレンジするくらいでいいかなと。確かにこのあたりはうまく扱えるようになると、早い者勝ちですがドヤ顔できます。

 ただ、演出効果系は派手さはあるものの、ユーザービリティの質が上がるものではなく、場合によっては悪目立ちすることもあるので、さりげなさは大事です。

 

 キャラ絵のようにキレイで大きいほど喜ばれるものはメモリ消費がネックです。

 テクスチャを効率よく使用するということは、そういったリッチなテクスチャをたくさんメモリに乗せるために、削れるものはとことん削る。でもクオリティは落としたくない。そのためにはシェーダーが頼もしい味方になってくれます。

 

 ゲーム開発は最後の最後まで仕様が揺れます。開発期間が長いと見飽きてきて手を入れたくなったり、先行リリースのタイトルと被ってしまったり、時流的にNGデザインになってしまったり。開発あるあるです。

 手を入れたくなったとき、ある調整によって連鎖的に変更しないといけないものが多くなることなんてしょっちゅうです。そうなると「妥協」の二文字が脳内に積みあがっていくことになります。

 シェーダーのパラメータを変えたり、ひと手間入れておくことでギリギリまで安全に調整できます。もちろんデザインを変えるのは大変ですが、UMGやブループリントをいじらずに見た目のガラリと変えることもできたりするので、やっぱり融通がきくというか、ゴリ押しができたりして臨機応変な対応がしやすくなります。そのためには使いこなせたほうが断然有利です。

 

 パラメータの使い方に慣れてくると、調整のしやすさと汎用化に気づくと思います。

 少しでもたくさんの美麗なグラフィックや、演出効果を実装するために、節約して切り詰めるところはシェーダーを補助的に使って、画面処理に余裕のある場面では大胆にリッチなシェーダーを組む。いろんなシチュエーションに対応できるバランス感覚を身に着けるためにもいろんなシェーダーを経験しておきたいものです。

つまり

 

工夫して試す

 

これに尽きます。

 

100本分を改めて眺めてみると、ゲージとしての仕組みはだいたい同じであることに気づくと思います。

Stepノードで 0 と1 の2色に分け、Lerpノードで着色。

まずは これを基本形として覚えておけばだいたい何とかなります。

 

あとはデザイン次第で継ぎ足ししてゆきます。

テクスチャに余裕があれば、UVを動かしてみたり。

汎用性については慣れるまではあとから考える方が良いです。最初は狙った仕様通りのものを速度重視で用意。いくつか溜まってきて同じような挙動のやつがあれば、まとめられないか検討する。共通項を見つけるような感じで少しづつ進めると安全です。

 

 

最後に

シェーダーを使わなくてもゲームは開発できますが、使えるようになるとUIデザイナーとしての QOL が少し向上します。

用意したデザインパーツをテクスチャにして復元するだけの簡単なお仕事。

で納得していてはもったいない。

せっかくUEという素晴らしいゲームエンジンを手にしたのなら、使いこなすことであなたのセンスをさらに引き出すことができます。たぶん。

ぜひいろんな表現、手法を見つけていってください。

この100本ノックがそのための足がかりになりますように。

 

ではでは

素敵なシェーダーライフを!

 

 

ゲージ100本ノック 100 《タネアカシ編》

なんとなく今まで紹介してきた方法を思い出しながら挑戦してみてほしいなと、ふと魔が差してしまい、前回の記事では見た目に盛り合わせた要素をヒントと称して軽く説明するにとどめました。

今更タネというほどもったいぶるようなテクニックがあるわけではないですが、どうやってるのか、というのを書いていきます。シェーダー(UEで言うところのマテリアル)は、作って動かした方が面白いので、ブループリントでチャージするカスタムイベントを作ったり、キーボードでゲージを増減させたりしてます。

パラメータがちゃんと想定通りの動きをするかを確認するのが目的ですので、実装にはあまり参考にならないものもありますが、その辺はご理解いただけると助かります。

マテリアルさえちゃんと動いているのを確認できれば、そのままアセットを正式な場所に持って行けばいいだけですし。

 

ではさっそく

 

100. ゲージ

まずは今回の動き

5種類の要素について説明していきます。

目コピチャレンジの答え合わせになればと思います。

 

 

体力ゲージ

パーツの構成については前回の記事で書いています。

メインのHPゲージなので、よくある2段階式にしました。→ 77.

ゲージの形状をテクスチャのアルファチャンネルで、カラーはシェーダーで着色。

テクスチャがアトラスなので、一部分を切り出すために VectorParameter を使っています。



 

ゲージが増えるときにのオーバレイエフェクトは、ゲージの着色部分で黄色の部分に適用します。

Timeで常に動かしています。模様のカラーがゲージと同じだと単色に見えます。

 

その模様のカラーを白色にすると目に見えるようになります。

黄色のゲージカラーと白色は、パラメータでブレンド量が調整できるようにしてあります。

白色は、Lerpノードの B のピンに入力してある 1.0 です。

タイミングが来たら一定時間フェードするようにブループリントから制御します。

 

タイムライン アニメーションを使ってもいいですが、ブループリントでフェードする方法を紹介します。

 

Float型の変数を 1つ用意して、初期値を 1.0 にして、徐々に減らしていくカスタムイベント。ゼロになったら終了します。

Set Timer by Eventノードは一定間隔で朱色のラインでつないだイベントを呼び出します。条件が外れるまで、再び自身を呼び出す形になっています。

このスタイルは個人的にテンプレートになってる感じで、タイムラインを使わないときはこのかたちが多いです。

 

加算する場合は 満タン かどうかを判定して、減算する場合は 0 かどうかを判定します。

スピードは、加減算の変化量と、Set TImer by Eventノードの Time で間隔を調整します。

 

 

 

 

数値

数字の動かし方は前回の記事で書いたので、今回はカーブさせる方法について。

当初Photoshopでグラデーション作って試してみたら、やはり精度の点で解像度どビット深度の調整でコスパの悪さが気になったので、エンジンのカーブアトラスを採用しました。

上図はPhotoshopで無理やり変形したので妙に歪んでますが、理屈としては、テクスチャUVのU座標に対してカーブするようにV方向をずらしていきます。

3桁とも横幅が同じなのに、傾斜が緩くなるとスリムに見えます。脳が無意識に奥行き感を補間してしまうのが原因だと思う。

傾斜の程度に合わせて、横幅の調整をすると違和感は減ると思います。(調整をさぼった言い訳)

 

まずは カーブアセットを作成します。

コンテンツブラウザで右クリックすると、Miscllneous というのがあるので中にあるCurve を選択します。  

今回レイアウトした数字の桁は 3桁カーブクラスは LinearColor を選択します。

Vectorでも問題はないのですが、ピクセルのずらす方向が+方向のみなのでマイナスの値が無いほうが扱いやすいと思い LinearColor にしました。

中のFloatが 4つあるので将来的に数字の桁が増えても 4桁まではカーブアセットを増やさなくても対応可能。

 

編集します

RGBAをそれぞれ、100の位、10の位、1の位と決めて、何となく曲げてみます。Aは予備。

アナログなやり方ですが、後ほどマテリアルに組み込んでから表示されたものを確認しながら微調整するので、最初は適当に進めます。

保存して、今度はカーブアトラスを作成します。

これも Miscllneous の中にあります。

編集ウィンドウを開いて、Gradient Curves のところに先に作ったカーブアセットをセットします。

ここにカーブアセットをセットします。複数あればそのたびに追加していきます。

このカーブアトラスは、カーブの内容をテクスチャにして書き出す仕様ですので、解像度があります。大きいほうが精度が高いものになりますが、そのぶんVRAMの使用量も大きくなるため、サイズを節約して切り詰めます。

Square Resolution のチェックを外すと Height が変更できるようになるので、まずは高さを切り詰めます。

今回のゲージでは 2つのカーブアトラスを使用したので、Heightは 2 にしてみたらResourceSizeがかなり小さくなりました。

 

カーブが準備できたので、マテリアルを用意します。

 

完成形

 

数字はテクスチャ内で水平に並んでいるので、1文字ぶんのスケールをかけて U方向にずらせば狙った数字が切り出せます。

タイリングしないように Clampです。

 

この数字のマテリアルは専用の表示になるので、ひとまず汎用性は考えないことにして、U座標に掛けるスケールについては、Constant ノードにしています。

 

数字ひとつのサイズは 32 x 128px 、テクスチャ解像度が 512px、

U方向に 32 / 5120.0625 ずつ並んでいます。

Constant ノードは 数字キーの [1] を押しながらクリックすると取り出せます。

 

Scalarパラメータの Index で 0 ~ 9.0 を受け取る前提で考えます。

その値に、1文字ぶん の定数 0.0625 を掛けると0~9の各数字のU座標になります。

 

V方向は、カーブによって変形させます。

CurveAtlasRowParameter ノードを利用します。

このノードに、カーブとカーブアトラスをセットします。

 

カーブで作ったグラデーションは水平方向でアトラス化されているので、

CurveTime ピンには TexCoordComponentMaskR をつなぎます。

CurveAtlasRowParameterノードの出力は LinearColor ということで Vector4 の状態。RGBA それぞれ 100の位、10の位、1の位なので、選んで個別に取り出すことはできるけど、じゃあ 一本ずつ取り出したバージョンのマテリアルを3つ作るのか、というとさすがに効率が悪いので、マテリアルインスタンスで運用します。そうすると、3桁分の数字に対してパラメータで切り替えればいいのです。

 

そこで ドット積(Dot Product)を使います。

Dotノードを使うとチャンネルマスクのような使い方ができます。

ドット積は 2つのVector4 を掛けて足し合わせるので、結果的に 4つのFloatが 1つに収束します。

このとき、 ゼロのあるところは掛け算で消滅します。この性質を利用します。

 

残したいチャンネルに 1 を、他のチャンネルを 0 にした Vector4パラメータ を用意してカーブから出てくる Vector4 にぶつけます。

 

例えば、Greenを残す場合 

 ( ↑マテリアルインスタンスで入力した状態 )

パラメータで切り替えられるようなったので、マテリアルインスタンスからでも狙ったチャンネルに絞ることができるようになります。

 

あとは、カーブの強さを調整できるようにして ScalarParameterノードを乗算しておきます。値が微妙な時に一気にブーストしたり繊細にしたりできるので便利です。無くても問題ないです。初期値に 1.0 を入れておくと、結果に影響しなくなるのもポイント。

今回はカーブの値の範囲を0~1.0 で作ったので 初期値を 0.1にしています。 

これを数字テクスチャの V座標から引き算(Subtract)します。

カーブは 正の値で作ったので、引き算することで、U座標に対する V座標が負の領域になります。(下図左)

画面に表示する際、四角形に収めようとするので、負の座標からサンプリングすると、結果的にピクセルが ↓ 方向にずれます。マイナスぶんがぐいっと押し出される感じ。

(下図右)

 

 

今回数字のために用意したマテリアルは以下。

左から、親マテリアル、100の位、10の位、1の位 

 

マテリアルインスタンスをレイアウトします。

カーブの具合を調整して整えます。

ちょっと横幅もいじってみたら、違和感がマシになりました。




レベルメータ風謎のチャージゲージ

一定の速度で溜まっていきます。使用したり戦闘不能になったりすると、ゼロに戻ります。
ゲージの形状がカーブしているのと刻みが不定なデザインを採用したので、均等で直線で進むゲージ 13. は使えない。そこで、グラデーションを自前で用意する方式を採用。

 

シェーダーではゲージのシルエットと、グラデーションという 2つのパーツを組み合わせています。

固有のゲージなので、汎用性は考えないことにして、テクスチャアトラスからUVを切り出すのに、Constant と、 Constatnt2Vector ノードで値を指定し計算しています。

 

ゲージの増減は、シンプルに グラデーションテクスチャに対してStepノードを使うだけ。

グレースケールの段階を均等に割ってブラシで塗って作るので、それなりの手間がかかってます。

(ゲージの外は雑でもバレない)

0 の黒を使うと、ゲージが空っぽにならないので、一番下は 0 以上のグレーにするのがコツ。

 

ゲージのカラーに カーブアトラスを使用しました。

0.3(30%)を越えるとオレンジから緑になる想定。

数字と同じカーブアトラスにセットしています。

事前に用意したカラーでゲージの色を変化させます。

15. と同じ要領でゲージ残量を CurveTime に適用します。

 

数字はときはカーブの全域を取り出して使いましたが、これは カーブの一部分を取り出して使うので、ゲージ量の ScalarParameterノードを直につなぎます。

 

 

 

チャージ式のお花アイコン

円グラフの動きは 12. で 2枚の絵が切り替わるのは 47-54. です。

 

お気づきの方もいらっしゃるかもしれないですね。

今回開始位置を90度回すのに、12. とは違うつなぎ方をしています。

符号は同じですが範囲が違います。

VectorToRadialValueノードが出力する、Vector Converted to Angle については、角度を数値化しているので、符号の向きが同じなら結果も同じになります。

 

実は最近気づきました。UV空間の値を渡すので、0.5なんて半端だ! という思い込みがあったのようです。なので、今回少しノードのつなぎ方変わっているのです。

思い込みは新たな途を切り拓くときの壁になるので気をつけないといけませんね。

 

V1-x ノードで 1 → 0 に変えつつ、UVを入れ替えて、-0.5 すると、UV空間が90度回転します。

円グラフタイプのゲージが完成。

絵柄をハートにすると「ゼルダのやつ!」とか言われるので注意が必要です。

 

 

ゲージが満タンになったときに、2タイプのエフェクトを表示しています。

テクスチャは右端にあります。

基本はゲージ 69. で紹介した方法です。

この時はテクスチャの左上が UVの 原点だったので、作り易かったと思います。

 

今回のはこのようにしました。

UVを -0.5 してすぐに Absをつないでいます。そのあとで切り出すための計算をします。

69.では 2を掛けて 0 ~ 1 にしていましたが、どうせスケールを掛けるので、0 ~ 0.5 のままにしています。

 

テクスチャアトラスはこのようになっています。

 

切り出したいパーツの横のサイズは 64px、テクスチャ解像度の横方向のサイズは 512px

ということで、U空間に掛ける値は  64 / 512 = 0.125 、V空間に掛ける値は 64 / 64 = 1

 

上にも書いた通りUV空間を 0 ~ 0.5 のままにしているので、ここで 2倍の値で掛け算することで帳尻を合わせます。0.125 → 0.25 で、 1.0 → 2.0 にします。

Offsetの値は、そのままの倍率で構わないので、U方向は 448 / 512 = 0.875

ScaleUV と OffsetUV を VectorParameterでまとめました。

結果オーライで、計算処理は少ないほうがお得です。

 

ここで、気を付けたいのが、テクスチャのバイリニア対策。

1px はみ出させてあります。

タイリング設定の Clamp と同じ効果を狙っています。

テクスチャアトラスをレイアウトするとき、切り出すサイズより1px 余分に残してマスクしています。99. の記事でUV範囲の内側に 1px 余白を、と書きましたがさっそく例外です。切り出す範囲の端まで絵があることが理由です。

Clampはテクスチャの端に効果がありますが、中は効果がないので、わざわざ手で対策してやることになります。

これでスキマが開くことなくキレイにつながります。

ただし、境界の位置が 4で割り切れない場合は、圧縮のダメージを覚悟する必要があります。色数が少なければ意外と大丈夫だったりします。

 

アニメーションについては、タイムラインで付けました。

拡大しながらフェードアウトしていきます。

 

 

満タンエフェクトのループする方は、ちょっとだけ小技を使っています。

中心から広がる動きは前回の記事で書きました。極座標で動かしています。

で、小技というのはこれ。

同じものが並ぶと、複製している感が出て安く見えてしまいます。そこで
WidgetがViewportに置かれたときに、初期状態の角度をランダムに変化させています。

 

このチャージ式のお花のアイコンは、1つずつ順番にチャージします。

チャージ完了時に通知するように、イベントディスパッチャーを追加しました。

 

チャージ完了時に呼び出すようにします。

 

親のWidgetブループリントのほうで事前にバインド(Bind)しておけば、通知を受け取ってくれるので、2つ目のチャージを開始できます。

 

Widgetお花1号! チャージを開始せよ。 お花1号 は完了したら通知するように。

・・・

お花1号: 完了しました!

Widgetでは お花2号! チャージを開始せよ。お花1号バインド解除。

      お花2号 は完了したら通知するように。

・・・

お花2号: 完了しました!

Widgetお花2号バインド解除。

 

というストーリーがこちら

Widgetでこのように構成しました。バインドは親から子に対して行います。子はイベントディスパッチャー作って呼び出すだけです。

 

今回見た目に動いていればよいということで、作り易さからチャージ処理を 子Widgetに丸投げしました。しかも、お花アイコンとエフェクトを一緒にしてしまったばっかりに、1つ目の満タンエフェクトの上(手前)に 2つ目が乗ってしまう結果になっています。

本当はチャージ用のお花アイコンとエフェクトは別々にしないといけないですね。

 

 

 

キャラ

キャラは特に難しいことはしていません。

テクスチャアトラスが長方形で、水平に 2種類が絵が並んでいるだけ。

 

U座標へのオフセットを ScalarParameterノードで切り替えられるようにします。

キャラは他にもたくさんいる想定で、テクスチャサンプラーはパラメータにしました。

体力がゼロになったときの演出として、 Dotノード(DotProduct)で一旦モノクロにして着色したカラーを Lerpノードでフェードできるようにしています。

 

モノクロ化は、NTSC加重平均法 というやつを採用しました。

固定の値なので、Constant3Vectorノードで
 X  0.298912

 Y  0.586611

 Z  0.114478

をテクスチャのカラーとドット積を使って合成するといい感じのモノクロにできます。

ただモノクロにしただけだとちょっと明るいので、

明度を下げて若干ヤバ味を足す感じで くすんだ退紅色を掛け算しました。

 

ダメージを受けた時の振動は、Widgetブループリントで揺らしています。

絵の内容によっては、テクスチャの解像度いっぱいまでピクセルがあるかもしれないので、シェーダーで動かすのはコスパが悪いというのが理由。

揺らすための余白が必要で、画面の描画領域がテクスチャ解像度より大きくとるか、余白を入れて絵を小さくするか、どちらにせよ透明部分も描画処理されるので、無理はしないほうがいいという判断です。

 

このカスタムイベントは、何かの値を判定して終了というのは無く、ずっと動き続けるので、止めるにはタイマーハンドラを介して止めます。

 

汗表現は、2パターンの絵を一定間隔で切り替えています。

シェーダーの Time ノードの値を加工して 0 1 の 2値にします。

そこにU座標へのオフセット量をかけてやると、パカパカするU座標が得られます。

 

パターンのサイズは 80px テクスチャ解像度は 256px

80 / 256 = 0.3125

2パターンの点滅ができました。

 

 

 

ここまでで、各要素の構造を紹介できたと思います。

最後に、キー入力の判定部分を紹介します。

簡易的なテストはレベルブループリントで、Inputノードを使うことが

キーボードの[D] で「ダメージ」、となりの[F]キー で「回復」 のカスタムイベントを呼び出すようになっています。

ダメージは毎回 60~180 の範囲でランダムな値になるようにしています。

Andノードを使って、ゲージがゼロ以上かどうかも判定に加えています。

 

 

 

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

体力ゲージがゼロになったあとの処理とか、遅れて追いつくように減らす処理とかは下図で説明に替えられたら助かるのですが・・・

メインはこんな感じです。あとお花のWidgetもあります。

この100本目のアセットだけ切り出して公開も考えてみようかな。いや、いっそのこと全部公開する?

 

 

最後に

100本目でラストということで 盛り合わせてみましたが、いかがだったでしょうか?

総集編的な位置づけで、いつもより駆け足気味な感じになっているかと思います。

ひとまずUIで扱えると役に立つと思われる範囲で、ゲージ制作に絡めたシェーダーの紹介をしてきました。途中ネタが尽きて弱音を吐いたり醜態を晒しましたが、無事走り切れました。

Xでイイネやリポストしてくださった方々、ネタを提供してくださった皆様、本当にありがとうございます。とても励みになりました。今でも本当に100本も作ったのか?どこか番号飛ばしてるんじゃないか? と不安になります。

UIを制作している方で、シェーダーを覚えたいけどどうしたら?というお悩みに貢献できたら幸いです。

実際に100本作ってみましたよ~という方から感謝の言葉をもらう未来を夢想しながらこの企画は終了したいと思います。

そのうち急に思いついて番外編とかおまけ企画とかやるかもしれません。

まだまだわからん、とか他にも取り上げてほしいものとかあればコメントとかで教えていただけますと有難いです。

決して読みやすいとは言えない内容だったと思いますが、お付き合いくださった皆様ありがとうございました。

 

ではでは

ステキなゲージライフを!

 

 

 

 

 

 

 

 

 



 

 

 




 

 

 

 

 

ゲージ100本ノック 100 《ヒント編》

ついにこの時が来た。2024年の6月16日から書き始めてから1年以上かかってしまった。途中ネタ募集したり余裕がなくなったりして継続が危ぶまれたけど、それなりに楽しめたので、頑張った甲斐があったなとしみじみ思う。

今までの集大成といえるかは怪しいけど、99本目までを頑張ってこなしてきたからには、これくらいのUI表示はもう作れるよね?という考えで、100本目はいろいろ盛り付けました。

 

ガッツリやるとボリュームがありすぎて、公開まで日が開いてしまいそうな気がするので、構成としては2部に分けようかと思います。

 

今回は 作ったものの紹介とヒントを書くにとどめて、詳細は次回ということでネタバラシ編とします。

テクスチャを #100 - Google ドライブ で公開しています。

チャレンジしてみたいけど、アセット用意するのがちょっと・・・という場合にご利用ください。

ファイル名については本文中に "faile_name.tga" のように表記しています。

 

ではさっそく

 

 

100. ゲージ

 

普通の棒ゲージはつまらないので、リボンをモチーフにしたり、ポップさを出したくてキャラを描きました。ゲームだからダメージを受けるのは当たり前ということで2枚用意したりして時間がかかりましたが、かなりゲームぽくできたと思う。

前回みたいに、ガッツリ仕様を考えたりはしてなくて、とにかくイロイロ作って盛り合わせました。

 

要素は以下の5種盛り

  • 体力ゲージ(遅れて減る2段式で回復あり)
  • 数値(体力の数値化)
  • レベルメーター風の謎のチャージゲージ(ダッシュ系?)
  • チャージ式のお花アイコン(スキル?)
  • キャラ2パターン(汗はオマケ)

 

 

 

体力ゲージ

間にキャラを挟み込むのでパーツが手前と奥で分かれています。

さらに、ゲージ、ゲージじゃない塗り部分、黒い枠線という構成。

テクスチャはグレースケールで作成して、シェーダーとブループリントで着色しています。

サイズは  256 x 512px  のグレースケールテクスチャ "tex_100_Gauge.tga"

 

立体ぽく見せるためにも全体にカーブしていますが、両端を垂直にしているので、ゲージとしての分かりやすさを担保しています。

遅れて減るタイプのゲージです。

 

ゲージじゃない塗りの部分はデザイン的に連続しているように見せつつ、ゲージとしてはつながっていないので、トーンを落としています。

とはいえ、ゲージの増減に連動させることで、ささやかながら満タンと空っぽの状態をフォローするようにしています。

カラーの変化に段階は無く、満タンかどうか、空っぽかどうか、で判定してグレーにしています。

パーツが分かれているので、ゲージとは別にブループリントで制御しています。

 

 

回復するときに、エフェクトを重ねるようにしました。

タイリングさせるので、テクスチャは小さく作っています。

サイズは  32 x 32px  のグレースケールテクスチャ "tex_100_GaugeUp.tga"

フェードはWidgetブループリントからの制御です。

 

 

数値

桁は3桁Maxです。

ゲージのカーブに合わせて、ちょっとカーブさせています。

幅は変えてもよかったかもしれない。

数字の変更は、ブループリントで桁を分解して変更しています。

 

マテリアルのパラメータにアクセスするには、Dynamic Material Instanceを取得しないといけないのですが、毎回やるのはコストが高いので、事前に変数化しておきます。

 

11/8 追記:「毎回やるのは」と思い込んでいたのですが、Dynamin Material Instanceがまだ作成されていなければ作成するという仕様で、複数個所でこのノードを配置している場合、最初に処理した場所で作成されるようです。なかったら作るということは、毎度有無をチェックしているということなので、ループ処理でこの判定はどれくらいコストになるんだろうか。変数化なんて考えずに Get Dynamic~をじゃんじゃんつないだらいいよ、というEpicのやさしさ?てことかな。個人的に、変数化しておくこのやり方が、明示的に準備して扱う感じがして好きです。恒吉星光さん情報ありがとうございます。

 

とりあえず3桁分。

 

ゼロ埋めにしたので、100以下と10以下で不要な桁のゼロを暗くするようにしました。

 

数値を受け取ったらマテリアルパラメータに送って数字のUVを変更しつつ、カラーを変更するカスタムイベント。

Selectノードにセットするアルファをゼロにすると見た目に消すこともできます。

 

 

サイズは 512 x 64px のアルファ付きカラーテクスチャ "tex_100_Number.tga"

 

数字の下の余白は、シェーダーで湾曲させてやろうと思って、意図的に確保しました。

 

余白なしでも湾曲させることはできますが、シェーダー内の計算が増えるので、ひとまず余裕を持たせた形で進めました。

 

湾曲には、カーブアトラスを使っています。

詳細は次回。

 

 

レベルメーター風の謎ゲージ

 

テクスチャはHPゲージの下部分が空いていたのでそこを利用。 "tex_100_Gauge.tga"

パーツは 枠 と ゲージ に分けて重ねて表示。

 

ゲージのサイズが小さく、分割線の間隔や角度が均等ではないので TexCoodノードのグラデーションを利用せず、テクスチャを使って増減させています。

あとから分割数や形状が変わると描き直さないといけないのがリスクになるけど、形のユニークさや位置は、プレイヤーのUI操作において学習コストを下げる効果があるので手間を惜しまないようにしたいものです。

 

カラーの変化に カーブアトラスを使っています。

詳細は次回。

 

 

チャージ式のお花アイコン

多分ボムみたいな一発スキル系だと思う。

自動でチャージして、満タンになると使用可能になる。

満タンになったことをアピールしたいのでエフェクトを2種類。

貯まった瞬間の大きいのと、準備OKという意味のループするやつ。

 

今回 2個並べました。

仕様として 1つ目が貯まってから 2つ目を貯めるというふるまいをそのままストレートに作ると大変です。

そこで 1つ分のふるまいをWidgetブループリントを作って、USER CREATED パーツとして配置しています。

 

サイズは 128 x 64px のアルファ付きカラーテクスチャ "tex_100_Bomb.tga"

 

エフェクトのテクスチャは数字の右のほうが空いていたので拝借。"tex_100_Bomb.tga"

1/4しかないので、復元しています。動きは UMG で拡大とフェード。

テクスチャのアルファチャンネルは真っ黒にしてあるので注意。

 

ループするやつは 64 x 64px のアルファなしカラーテクスチャ "tex_100_BombEff2.tga"

 

解像度が低くアルファブレンドだと色が濁るので、

BlendModeを 加算(Additive)にして重ねています。

この Blend Mode 設定は UMGからできないので、Materialエディタで設定します。

 

 

中心から外に向かって放射する動きは VectorToRadialValueノードで。

 

100本ノック企画で紹介していなかったので、載せておきます。

 

 

 

キャラ

 

テクスチャは 512 x 256px のアルファ付きカラーテクスチャ "tex_100_Char.tga"

 

 

 

 

ダメージを受け時の効果はこんな感じ。

256 x 256px のカラーテクスチャ  "tex_100_CharEff.tga"

将来的にバフやデバフのエフェクトパーツを追加する想定で大きめです。

 

 

2パターンの絵を切り替えています。ひとつの大きさは 80 x 72px

シェーダーで常時動き続けています。

普段は非表示で、ダメージを受けたときだけキャラの切り替えにタイミングを合わせて一定時間表示します。

 

 

ゲージ残量がゼロになると、キャラのカラーを変化させています。

シェーダーでモノクロの見た目を作って、Lerpノードで切り替えています。

切り替えの動きはWidgetブループリントで制御しています。

 



 

要素の説明は以上です

テクスチャ素材を公開しているので、目コピにチャレンジしてみてはいかがでしょうか。次回の記事で答え合わせを書く予定です。

UIとしてそれっぽく動かそうとすると、シェーダーだけではどうにもできないふるまいがたくさんあるので、ブループリントに慣れていないと難しいかもしれません。でも、マテリアルのパラメータをいじって、想定通りの動きを見せれば、UI向けのシェーダーの理解に自信を持っていいと思います。指定書(指示書)でエンジニア(プログラマ)に渡せます。

 

キャラや背景などの3Dモデル用のシェーダーに比べたら、かなり限定的な使い方ですが、ライトやカメラ、ポストと無縁なUI表示にはこれくらいで十分です。あとはひたすら応用とトンチです。

というわけで次回最終回

 

ではでは

ステキなゲージライフを!

 

 

 

 

 

 

 

 

 

 

ゲージ100本ノック 99

いつの間にか夕暮れも早まり、ようやく夏の勢いも少し弱まってきたように感じていますが、ここしばらく夏バテか何かわからない不調を引きずりつつ過ごしています。

この企画も残すところ2本ということで、実装を意識した構成にしてみようと思います。

プロダクションごと、プロジェクトチームごとに文化やカスタム環境があると思いますが、あえて標準的なエディタ環境でできる範囲での個人的なおすすめということでご理解いただけると助かります。

 

シェーダーの理解と学習のために、意図的にシンプルなデザインで進めてきましたが、実際にリリースされているゲームはユニークなデザインばかりです。今回はそういったレベルに近づけるよう実践的な制作手法を、デザインからテクスチャアトラスを作ってUMGで構成するところまで紹介します。

仕様をデザインに起こして、テクスチャアトラスを作るためにPhotoshopを使用、大量のデザインパーツを実装するための汎用的なシェーダーを作るところもあるので結構なボリュームになってます。

最後のほうに、サイズの違うゲージを同期させる方法があります。

 

 

仕様

スキルゲージを作ります。

  • 時間でチャージ
  • 満タンになるとスキルを発動できることを教えたい
  • 発動するとゲージはゼロに戻る
  • 技名(未定)を一緒に配置したい

 

デザインする

まずは最低限必要な要素で構成しつつ画面のレイアウトとデザインを決めます。

レイアウトはゲームのジャンルによってだいたいスタンダードなパターンがあったりしますが、そこは縛られないよう疑問をぶつけながら挑みます。

この場所に置く理由は? キャラの向きやカメラの視線との相性は? 操作への集中度は? などなど。

デザインは、ぶっちゃけ世界観に合ってりゃいいのでは、と考えていますが、新規性やユニークさ、キャッチーさ、分かりやすさ、などなど 考えながらラフでデザインを作っていきます。

イデアがひらめいたらこの時に取り込みます。

まずは カタチ と 色 を慎重に選ぶフェイズ。

想定される状況の表現を詰めます。

どうやら仕様を満たせそうです。

 

今回の仕様は『満タン』か『満タンじゃないか』の2つの状態が表現できれば条件は満たせるので、イベントとしては、満タンの時にトリガー(発火)されて、発動するまではループ。目立たなければこのループ中に演出を盛ればいい、という風に考えます。

 

デザインはこれで行くぜ!となったら、

次にゲームの仕様について精度を上げたい。

デザインではなく実装方法に影響するので関係者と握ります。

  • 技名は全体でいくつになるのか
  • ゲージの長さは固定?
  • 発動禁止やチャージ不能または停止のような状況はあるのか

3つ目は見た目の話ですが、状況的に頻度が低くデザインを考え直すほどの要素ではないので、このタイミングの確認で大丈夫。

大事なのは大まかなデザインで雰囲気がつかめるものであれば、できるだけ速やかに実装するのが理想的です。

 

決まってなくても焦ってはダメです。急いで決めてもいいことはないです。

少し多めの余裕を意識して作業を進めておくと、急に決まっても慌てずに済みます。

むしろ、このゲームだったらこうなるよね、というの想定して先手を打つことができるようになりたいものです。

 

 

テクスチャアトラスを作る

デザインをパーツに分解します。

表示方法の違いによって分けます。

まずは単純に 動くか動かないか(動かないけど表示タイミングの違うものも含む)、次に描画のしくみが違うもの(テクスチャの種類やシェーダーの着色方法など)

 

今回のデザインはこのようになりそうです。

 

パーツの分解方針が決まれば、次はパーツのサイズを確定していきます。

デザインの上から半透明のレイヤーを重ねます。

これは UVの範囲をアタリとして視覚化する作業で、選択範囲で囲って分かりやすいカラーで塗りつぶします。

このとき、UVで切り取る範囲を想定して 4の倍数で範囲を決めます。

中のピクセルの周囲に1px以上の余白(アルファの透明部分)をあけます。

1px以上の余白を含めて4の倍数サイズということです。

 

この作業の目的は次の2つ

  • 4の倍数はテクスチャ圧縮による劣化対策
  • 1px以上の空きはバイリニアフィルタによる にじみピクセルの侵入を回避

 

この数値はあくまでもデザインの話ではなく、テクスチャをキレイに画面に描画するためのテクニックです。

 

将来的にグラフィックチップの性能や仕組みが変われば、変化していくと思います。

近年のコンソールやPCは BC7 とかの BCxシリーズ がほとんどで 4x4 のブロックサイズが多いと思います。

ちなみにモバイル系プラットフォームで ASTC圧縮を使っていると、ブロックサイズが 5x5 とか 6x6、10x10 などいろんなサイズが用意されているようです。状況によっては 5の倍数を検討することもあるかもしれません。劣化度合を考えると、UI表示で 大きなブロックサイズを選択することはないと思いますが・・・

 

ちょっと話が逸れましたが、だいたい下のようになりました。

また、可変するかしないかで、余白の大きさも検討します。

シェイプを使うと小数になりやすく誤差やピクセルのブレが出やすいので、できるだけ、 選択範囲→塗りつぶし を強くオススメします。

 

 

作業の能率化のためにレイヤー名をつけるのをおすすめします。

 

UVのアタリが確定したところで、アトラス用のテクスチャを用意していきます。

Ctrl キーを押しながら レイヤーパネルのサムネをクリックすると、レイヤーのピクセルのある所を選択範囲として検出してくれます。

情報パネルを確認すると、サイズがわかります。

 

ゲージの枠を含めた幅が 384px だったので、直近のべき乗サイズは 512px。

高さが 52px で 2本で 104px、直近のべき乗サイズは 128px。

ということで まずは 512 x 128 の長方形で作り始めます。

 

UIで扱うテクスチャは基本的に Mipmap を使わないので、べき乗じゃなくても大丈夫です。ただ中途半端なサイズのテクスチャは、勝手にピクセルを削られたり補充されたり、UVの計算で割り切れなくて誤差の原因になったり、と注意が必要です。べき乗と4の倍数は結構相性がいいのでリサイズの時でも安心感があります。

 

ゲームのUIは、メモリの割り当てが少ないことがほとんどなので、同時にメモリに常駐する者同士でアトラスを組むとキャッシュに無駄が出にくくなります。

場合によってもっと大きなテクスチャアトラスの一部に組み込むこともあります。

今回はスキルゲージだけの構成で進めようと思います。

 

技名の表示については、ゲージではなくゲームシステムに依存する存在で、コストや期間でキャラ数が減ったり、トレンドやローカライズで文字量が変化したりと内容について不確定感が強いので、別管理で調整できるよう別のテクスチャアトラスにします。

これもデザインをみて、一つにつき 256 x 48 の右詰めでよさそう。ということにします。

 

シェーダーで UVを切り出すときに、テクスチャサイズ(解像度)に基づいて計算します。なので、テクスチャサイズを気安く変更しづらいというというのが、テクスチャアトラスを使う上でので一番のリスクだと言っていいと思います。

 

作るべきサイズが決まれば、さっそく新規のドキュメントを作成。

ここに、デザインパーツをドラッグ&ドロップするのですが、UV領域を決めるときの半透明のレイヤーと一緒に移動させます。

ゲージの枠とゲージ本体をドロップしたら、

スキマを開けずにぴったりくっつけます。

近くまでマウスで動かして、残りはキーボードの矢印キーで ちょいちょいと動かすとストレスが少ないと思います。

 

残りのパーツは、ちょっとしたコツが必要です。

ぼやけていたり、細く尖っていたりするデザインは、ほとんど透明で視認しづらいピクセルが存在しています。UVのアタリをとるときに、うっかり削ってしまうことが多々あります。

不透明度が 50%以下のピクセルには 選択範囲の表示も省略されます。(Photoshopの仕様です。実際にはきちんと選択範囲に含まれているのでご安心を)

 

こういうときは レイヤー効果の『境界線』を利用します。外側に2ピクセルほど設定するとUVのアタリを確実に取れるようになります。

これもテクスチャアトラスのほうに持っていきます。

 

テクスチャアトラスは レイアウトはこんな感じでOK。

 

DirectX系のグラフィックでは左上が原点(0, 0)なので、左上準拠で詰めますが、ゲージのようなデザインは若干長さや形状にゆとりを持たせておいたほうが将来的に保険となってくれます。デザインが完全に確定するまでは、種類の違うパーツはぴっちぴちに詰めないのがコツです。

 

次にアルファチャンネルを作ります。
その前にレイヤー整理しておきます。

ドラッグ&ドロップした UVのアタリが散らばってしまっている状態。

UV範囲のレイヤーのみを Ctrl キーを押しながらクリックして複数選択しておいて、

Ctrl + G でグループ化します。

 

ついでにレイヤーのカラーとUVのアタリで塗りつぶしたカラーをなんとなく合わせておきました。

この小さな手間は、そのうち効いてくれる日がやってきます。たぶん。

グループ化しておくと、テクスチャファイルとして書き出すときに、ワンクリックで非表示にできるからです。

 

ということで、UVを非表示にして、新規レイヤーを一つ追加します。

 

このレイヤーを選択した状態で、Ctrl + Shift + Alt + E キーを押します。

 

表示レイヤーの状態をラスタライズできるショートカットで、他のレイヤーには影響がないのがウリ。

新規レイヤーを作らなくても自動で作ってくれるのですが、フォーカスしているものやレイヤーの種類によって作ってくれない時があるので、確実な方法を紹介しました。

 

 

このレイヤーのサムネをクリックして選択範囲を読み込んで、

これをアルファチャンネルとして「保存」します。

コツは、最初だけ Ctrl 押しながらクリック。2つ目以降は Ctrl + Shift でクリックです。

 

下は チャンネルのパネルを使った「保存」。

メニューからは 選択範囲 > 選択範囲を保存(V)

ダイアログが出るので、OKすると作成されます。

 

アルファチャンネルを作るために一時的に作ったレイヤーは捨てます

残しておくと、半透明の部分が濃く見えるようになるのと、調整作業に支障がでるからです。

 

 

あとはアルファ抜きをキレイにするために、背景色 をパーツの下にUVの範囲で塗ります。パーツで使用しているピクセルのカラーを範囲いっぱいまで拡げます。

 

アルファチャンネルはこうなってます。

 

塗りつぶす理由は図で説明したほうがわかりやすいと思います。

これがアルファブレンドというやつです。

RGBのカラー情報とアルファ情報が別々に切り離されて処理されるので、対策が必要なのです。

RGBとアルファを正しい知識できちんと整えてやるのは UIを開発するうえで大変重要です。

 

さて、アルファ抜きの状態で書き出せるのが PNG形式。*1

ですがPNGを使わない想定で説明しています。

 

ようやくゲージのテクチャアトラスは完成です。

 

続けて

技名も作っておきます。

技名は 2種類で足りるという想定でいくことにして、フチドリとカラー(ゲージ)を交互に並べて完成にします。詰めてみて微妙に入らなかったりすると、最適な解像度と分割数を再調整します。余白は有るほうがあとから融通が利いて安心です。

最初からタイトに詰めすぎないのがポイント。

同じサイズで統一すると、マテリアルのパラメータで切り替えるのが簡単になります。

 

同じUVサイズが並ぶときは、UVのアタリを1枚のレイヤーにまとめたりもします。

 

これで材料は準備できました。

 

 

汎用シェーダーを作る

エンジンにインポートしたテクスチャアトラスは、箱から出したばかりのプラモデルのような状態。必要なパーツだけを切り出すシェーダーを用意します。

これを汎用として、一つ作っておけば、あとはマテリアルインスタンスで対応できるようになるので、メモリの節約にもなります。

 

まずはパラメータ満載で作ります。

ScalarParameterノードの ScaleU ScaleV は切り出したいサイズを指定するパラメータで、本当は繰り返す回数を意味する ”Tiling” というワードのほうが正しいと思いますが、ノードネットワーク的に掛け算するので、"Scale" にしました。

初期値は 1.0 にしておくことをお勧めします。

ゼロだとUV空間が消滅した状態です。マテリアルインスタンスで値を入れるので、とくに不都合はないのですが、そもそも 0 にするメリットは何もないです。

 

一方、Offset のほうは ゼロのままででOK。



テクスチャもパラメータ化できます。

TextureSampleParameter2D ノードです。

ヘッダに Param2D の文字があります。

マテリアルインスタンスから任意のテクスチャをセットすることができるようになります。

 

あとは保険で、アルファチャンネルに ScalarParameter ノードの Opacity を掛けています。

UMG にプロパティ Render Opacity があるので、なくても構わないのですが、マテリアルインスタンスを活かして、バリエーション違いを作ることがやりやすくなります。

 

カラーについても掛け算しておくのも有効です。

 

これでマテリアル ”汎用テクスチャ切り出しシェーダー” は完成です。

プロジェクトにこれがひとつあれば、テクスチャアトラスのほしいパーツをいくらでも切り出せます。

さっそくマテリアルインスタンスを作っていきます。

 

サイズ はレイヤーのサムネをクリックすると情報パネルに表示されるので、その W と H をテクスチャ解像度で割ると、それぞれ ScaleU ScaleV になります。

 

切り出す位置は、テクスチャの左上からの距離を調べます。選択範囲をとると W と H が判るので、それをテクスチャ解像度で割ると、それぞれ OffsetU OffsetV になります。

 

割り算するのは、エンジンの入力フォーム内でやるとラクです。

U は テクスチャ解像度の W と、 V は H とそれぞれ計算します。

 

この計算方法でパーツごとにパラメータをセットしたマテリアルインスタンスを用意していきます。

 

ゲージの枠はこんな感じです。

パーツのサイズが 384 x 56

テクスチャアトラスの解像度が 512 x 128

場所は左上なので、Offsetはどちらも 0 で初期値から変更なし

 

マテリアルインスタンスができたら、UMG のキャンバスに配置していきます。

Brush のプロパティ Image Size に表示したい大きさを入力します。

 

配置する親のパネルが Canvasの場合は、 Size to Content を有効にすると、Image Size を適応してくれます。

有効にすると Slot(Canvas Panel Slot) にあるプロパティ Size X と Size Y は無視されます。

 

Ovarlay パネルの場合は Size to Content が無いので、Brush の Image Size がそのまま利用されます。

 

デザイン通りに組みます。

 

次にゲージのシェーダーを用意します。

 

テクスチャでゲージを作る場合、先にも書きましたが、バイリニアフィルタ対策で、1px以上の空きをつくることが基本です。ゲージの増減は 0~1 なので、空きの分の誤差を吸収するのが Lerpノードとそのパラメータです。

これも初期値をきちんとセットしておけば、事故は防げます。

 

UVの計算をするパラメータを Scale と Offset をひとまとめにしました。

ScalarParameterノード ではなく VectorParameterノードを使っています。

(R, G, B, A) の 4つの Float を ( ScaleU, ScaleV, OffsetU, OffsetU ) の順でつないでいますが、順番を覚えていないと大変です。なので、チームやプロジェクトでルールを作って決めておくのをオススメします。

マテリアルインスタンスで値を入力するのですがラベルがRGBAなのです。

 

OffsetV だけとか パラメータを個別に扱うことが多そうなら、ScalarParameterノードで組みます。タイムラインやブループリントから扱うときにシンプルに操作できます。

 

一度パラメータをセットしたら動かさない場合や、同時に複数のパラメータを操作するときなどは、VectorParameterノードで組みます。

 

ゲージとしてのシェーダーは 21. とほぼ同じです。

 

ゲージ用の汎用マテリアルは完成です。

 

 

マテリアルインスタンスは、後から親マテリアルを変更することができます。

Parent のところを差し替えます。

 

パラメータの種類と名前が食い違う場合、残念ながら入力していた値は引き継がれず消えてしまいます。

親マテリアルを差し替える機会はそれほど多くはないと思いますが、パラメータの命名にルールを作っておくと少しは手数が減るかもしれません。

特に 透明度 は Opacity か Alpha かでブレやすいです。

 

 

差し替えるときのプチテクニックがあります。

変更後(先)のマテリアル側にいったんダミーで同じ名前のパラメータをつないでおきます。

この状態で差し替えると、過去に入力してあったパラメータが維持されるので、コピペできます。

パラメータの移行が終わったら、追加したノードは不要なので消します。また同じような移植の機会があるのであれば、いったん接続を切っておくといいです。

マテリアルインスタンスからはパラメータは消えます。

 

 

技名のゲージも同じように親マテリアルを切り替えます。

 

準備はできました。

動かしてみましょう。

ズレてますね。

 

シンクロさせる

このズレを マテリアルインスタンスのパラメータで補正します。

 

パーツの位置関係を調べるとこうなってました。

技名のゲージは短いので、ゲージの増減を合わせるには、差分を計算します。

表示サイズは256なので、

左側は 0 よりも左、 マイナスになります。 112 / 256 で 0.4375  RangeMin は -0.4375

右側は 1.0 以上になります。 16 / 256 で 0.0625  RangeMax は 1.0625 

 

ここはテクスチャの解像度は関係なく、表示サイズと表示位置の関係で決まります。

ちょっとややこしいですね。

 

 

シンクロできました。

RangeMin、RangeMax はテクスチャ使用時のアルファに余白のあるゲージタイプのための調整用パラメータですが、こういった補正にも利用できます。

 

ゲージの増減はこれで完成です。

あとは、満タン時の演出を追加するだけです。

 

満タン時の演出をつくる

UMGのタイムラインでループアニメーションを作って、満タンの時に表示開始。

スキルを発動したら非表示にします。

 

RenderTransform のScale を変更するだけの単純な拡縮アニメーションですが、UVの範囲に収めた絵が、うまく中心に来てくれるとは限らないので、微妙にずれることはよくあります。

動かしながら、RenderTransform の Pivotを微調整します。

Pivot の数値は UVと同じように 表示パーツの左上が (0, 0) です。

 

 

 

 

今回は思ってた以上にボリュームが大きくなってしまいました。

ある程度実装経験のある方には、テクスチャ作成のあたりは退屈な内容になってしまったかもしれません。

 

シェーダーを扱うということは、効率よくマテリアルのアセットを管理できてこそなので、親マテリアル と マテリアルインスタンスの関係性について、より実用的な内容を目指して書いてみました。あとはテクスチャアトラスの重要性について書きたかったのもあってPhotoshopのオペレーションも含めて書きました。

これを機に実装できるマンが増えたら嬉しい。

実際にUIを作るのは、デザインするのとは別のフィードバックも得られます。

絵を作ってあとヨロ、はもったいないと思う。

 

 

長くなったので、ちょっと内容がおかしくなってる箇所があるかもしれません。

解りにくいとこや意味が不明なところなどあればご指摘いただければありがたいです。

 

いよいよ次がラストです。

 

ではでは

素敵なゲージライフを!

 

 

 

 

 

 

 

 

 

 

 

 

 

*1:

PNGだったら、背景色を置かなくてもいいのでは?という疑問が浮かぶと思います。

エンジンにインポートされたPNG画像はそのままのクオリティで画面に出ることはありません。

 

PNGは RGB だけを抽出すると半透明の部分にもカラーがしっかり残っているのがわかります。ところが 完全に透明の部分はカラー情報に意味がないため、(255, 255, 255)の白か、(0, 0, 0)の黒が入れられることがあります。

さらに、シェーダーを活用するようになるとRGBA 4つのチャンネルをフル活用したくなるのですが、その場合、PNGでは必ずアルファが 0 以上じゃないとカラー情報が維持できません。

また、BCx系に圧縮されるコンソール系やPCゲームでは、PNGを使うと予期しない場所に勝手に入れられた白いゴミドットがうっすら画面に現れることがあるので注意が必要になります。

PNGは便利ですが、時として描画の不具合の元になるので、全ての場面で安心して使えない。というのが僕の見解です。使っちゃダメという話ではなく、リスクを理解したうえで使用しましょうという話です。

 

ゲーム開発向けというと DDS が比較的万能感があるんですが、書き出しやプレビューの環境が簡単には揃わないのが残念です。 

ゲージ100本ノック 98

ぷちコンの制作に集中してたので前回の記事から一か月も更新が止まってました。

今回もマテリアルインスタンスを使ったケースです。ゲージ自体はシンプルにバータイプで増減するだけ。このゲージの増減に連動するようにエフェクトを纏わせます。

ゲージをじっくり見てられないような忙しいゲームで効果が期待できます。

なんか変化があったけど増えた?減った?というのが視界の端で分かりやすくなります。

サンプルとしてプラスとマイナスにしたけど、マイナスのカタチがシンプルすぎてマイナスというよりただの棒に見えてしまう罠。1枚のテクスチャで2パターンを切り替える仕様なのでいろんな図形を試してみると何か発見があるかもしれません。

 

98. ゲージの増減でエフェクトを乗せる

この種の動きは普通ならエミッターを置いてパーティクルを飛ばしたり、プログラマにお願いしたりするかと思います。

表示時間は短めを想定しているので、あまりリッチなつくりはちょっともったいない気がします。まずは軽く作ってみて繰り返しが気になるようだったら、改めてプログラマに依頼するのでもいいかと。

 

ある程度散らばった状態で表示を開始したいので、マテリアルインスタンスで時差を作り、ランダムっぽく見えるようにパーツを配置します。

移動アニメーションはシェーダーで常に動いている状態にします。

 

テクスチャは 64x32px

タイリング設定は Clamp

これをV方向に動かすだけなのですが、表示するときには長方形にするので、スケールを調整する必要があります。

とりあえず今回は縦横のバランスを 1:3 にしました。

U方向は、テクスチャの半分を使うので、スケールは以下のようになります。

あとはTimerノードで 上下方向へ動かすのですが、スケールがかかっています。

見える範囲を端から端まで移動させるには -3.0 ~ 1.0 の範囲で移動するとよさそうです。

 

絵的にも方向もプラスとマイナスを切り替える必要があります。

この切り替えタイミングは、それほど頻繁ではないので、パラメータで受け取ることにします。

とりあえず動かすところまではこんな感じ。

図の下のほうにある ScalarParameterノードの Offset は、移動位置をずらして差をつける目的で追加しています。マテリアルインスタンスでバリエーションを出すのに利用。

 

プラスとマイナスを切り替えるときに、3つの値(上図の矢印)を一気に変更するので、VectorParmeterノードを使いたいところ。ただ、複数配置するマテリアルインスタンスたちに対して 一度に全部変更するとなると、前回同様パラメータコレクションを経由する方が簡単です。

というわけで ラメータコレクションを用意します。

RGBAとなっていますが、用途は自由。今回は

R: U方向のオフセット(0 だとテクスチャの左半分、 0.5 だと右半分)

G: V方向の移動 始点

B: V方向の移動 終点

A:全体のアルファ値

(アルファは最初から 0 にしていると、レイアウトしづらいので、今はいったん 1.0)

 

G と B の値を逆にすると移動方向が反転することになります。

ゲージが増減する際に、まずこのパラメータをセットするようにします。

 

これをマテリアルに組み込むとこうなります。

プレビューは正方形なので縦につぶれて表示されてます。

 

あとはここに、上下方向のグラデーションマスクを作って乗算します。

0.5を引いてAbsノードで絶対値にした状態では 0 ~ 0.5 のグラデーションで、さらに中央部が 0 になっています。

1-x(OneMinus)ノードを使わなくても SmoothStepノードでいい感じにできます。

SmoothStepノードは、 Max が 1.0 に、Min が 0 になるように計算して結果を返してくれます。こういう線対称なグラデーションは何かと使う機会が多いので覚えておくと重宝します。

 

このグラデと、バリエーション用の Opacityと、パラメータコレクションの Alpha をまとめて掛けると完成。

これをParentMaterial(親マテリアル)として、右クリックしてMaterialインスタンスをいくつか作って並べます。

 



チェックをつけると値を上書きすることができます。

チェックを外すと、マテリアル内の初期値が使われます。

 

透明度に変化をつけて少し重ねたり、スピードの近いものを離したりしながら、

いい感じに配置します。

 

マテリアルはこれで準備完了です。

あとは Widgetブループリントから表示タイミングやフェードをコントロールすればいいだけです。

 

 

ここからはテスト用のWidgetブループリントを用意して動かせるようにします。

 

適当なレイアウトです。

 

 

テクスチャの見た目と移動方向を切り替えるカスタムイベントがこちら。

動きは 上か下の2択なので、Boolean型の 入力ピンで方向を受け取ります。

勝手に決めますtrue で上! false で下!

右上の SetVectorParameterValueノードで マテリアルパラメータコレクションに書き込みます。

青色の ParameterValue ピンからドラッグして MakeColorノードを取り出したら、今度はのピンから SelectFloatノードを取り出します。

A は trueのとき 、B は falseのときに出力されます。

実装するときはプログラマに相談して決めるといいです。

 

このカスタムイベントをゲージが増減する直前に呼び出すようにします。

このときアルファ値を 1.0 にすることで 見た目に出現させています。

ということで、マテリアルパラメータコレクションのアルファ値はこのタイミングで 0を初期値にします。

 

 ここは UI制作の数ある闇のひとつでもあります。

誰が、どこのプロセスが、タイムラインが、最後の値をセットもしくは更新するのか。この辺のポリシーをあいまいにしたまま作っていると、意図通りに表示されなくて原因調査と対策に無駄な時間を使うことになったりするので恐ろしい。

 

実は

マテリアルパラメータが Vector型でひとつ余ったので、アルファフェード用にしましたが、Panel の中に入れてしまえば、Panel の Render Opacity でまとめてフェードがかけられます。

 

今回はVector型のパラメータを一部だけ書き換えの方法を紹介したかったのでマテリアルパラメータコレクション を使った アルファフェードで行きます。

 

ということで消す方法。

いったん Get して再び束ねて Set するかたちになります。

A だけそのままつながずに、0 を入力します。

Set ノードの Parameter Valueピンが選択して渡せるようにできたらいいのですが将来に期待しましょう。

Get ノードのほうは、Breakノードでバラしてもいいのですが、


ノードが増えるので、Split Struct Pinで分解します。

Set ノードのほうは、ParameterValueピンからドラッグすると Make Color ノードがすぐ見つかります。

 

この方法は、ある瞬間の値を部分的に加工したりと介入できるのでおすすめです。

 

ここまでに用意したもので、実装は可能ですが、キー入力してテストするところまで作っていきます。

 

まずゲージの増減する処理を、テクスチャの見た目と移動方向を切り替えるカスタムイベントの続きとして追加します。

拡大すると、

GaugeValue という Float型の変数を新しく作っています。

このカスタムイベントが呼ばれるたびに、0.01 ずつゲージを増やしたり減らしたりします。

 

 

次にアルファでフェードアウトするカスタムイベントを用意します。

AlphaValue という Float型の変数を新しく用意。フェード用に減らしていく使い方。

0 と同じかマイナスになったら、false になってフェードアウト終了。

 

0.02秒おきにこのカスタムイベントを呼び出します。

Set Timer by Eventノード の ReturnValue ピンから Promote to variable(変数に昇格)させておくと、タイマーを外から止めたりできるようになります。

AlphaValueの減らす量と、このタイマーの時間間隔を調整してフェードの印象を調整します。

 

Timeは ”秒” で指定するので、当面は 0.016秒より速くするのは注意が必要です。

コンソールゲームで快適とされるフレームレートが 60fpsです。処理負荷の状況に応じてフレーム更新タイミングがが可変というのも一般的になっています。 60fps は 1/60秒で描画が更新されます。それより速いと画面に反映されないタイミングでの計算が無駄になったり、更新回数の多さが、全体への負担になるので注意です。

 

ついでにこの部分、関数にしてみましょう。

下段のノード3つを選択してハイライトしたら、どれでもいいのでノードの上で右クリック。

コンテキストメニューCollapse to Function(関数化) というのがあるので選択。

関数のグラフが開くので、適当に命名。UpdateAlpha としました。

関数なので外からの値(引数)を受け取れるようにします。

この関数を使って、カスタムイベントを整理してみると、

すっきり。
コピペが簡単だとはいえ、関数化することで、同じ仕事をするノードのグループに名前を付けることになるので、見やすく、また流れがわかりやすくなります。変更や修正も1か所触るだけで済みます。なので同じ内容のノードのカタマリが複数現れたら、関数化を考えるといいですよ。

 

 

最後に、キー入力を受け付ける処理

キーを押している間はゲージが動くようにしたいので EventTick を活用します。

プロジェクトによっては Widgetブループリントで EventTick が無効にされている場合があります。つなぐことはできますが、コンパイルするとエラーになります。その場合  ProjectSettings で有効にします。ProjectSettingsが有効でEventTickノードが見つからない場合は、ClassDefault の 詳細設定 Performance > Tick Frequency が Never になっているかもしれません。Auto にするとコンパイルできるようになります。EventTickノードはほかのノード同様に検索で取り出せます。

 

よく簡易的なテストなんかでお世話になる Inputノードは、キーを押した時と離した時にそれぞれ 一度だけしか発火しません。

押したときにフラグを true にして、離したときにフラグを false にするようにしておいて、EventTick のところでそのフラグを判定して分岐するというのもありですが、判定するキー専用のBoolean型変数を用意しないといけないのがちょっとアレなので、今回は、PlayerController から Is Input Key Down Was Input Key Just Released  を使って検出します。

キーを離したときを検出するノードを探すとき、現在形と過去形になっているので、ちょっと雰囲気の違いに戸惑います。

UI関連でキー入力関連のワードはたいてい押したときは Down か Press、離したら Up か Release あたりを覚えておくと、いろんな開発環境で助けになると思います。

 

EventTick は毎フレーム駆動します。(確かWidgetブループリントのTick速度を変えられたと記憶していますが今回はテストなので)

EventTickが駆動したときに キーを押しているかどうか判定して true だったら false だったら、で条件分岐させます。テクスチャの見た目と移動方向を切り替えるカスタムイベントを呼び出しています。

押している間、何度も true になるので、何度もカスタムイベントが呼ばれます。

そして、その都度 Float型の変数から値が増減されるので、結果ゲージが動くことになります。

 

すでにフェードアウトが動いているかもしれないので、保険的にタイマーをクリアします。

Set Timer by Eventノードで タイマーをセットすると、彼は勤勉なので、途中で事態が変わっても動き続けます。なので 事前に変数化して用意しておいたTimer Handle を使って介入するのです。Clear and Invalidate Timer by Handle 以外にもいろいろ用意されています。

 

 

キー押している間は常にカスタムイベントが呼ばれ続けますが、指を離した次の Tickタイミングで Is Input Key Down の判定が falseでスルーされてきます。

ようやく Was Input Key Just Released が true になります。 

ということでフェードアウト開始。

 

指を離して一定時間後に、フェードアウトさせるために、以下の処理を追加。

前のフェード開始タイマーが、待機してるかもしれないので、ここでもTimerHandleを追加しておいて、強制的にクリア。

Set Timer by Event 便利なのでよく利用するのですが、タイマー発動中に不意に連続で呼ばれると表示タイミングがバグって見えることにつながるので、ハンドラーを用意してコントロールするのが重要です。ちょっと面倒に思うことがありますが、UIってほとんどがイベントドリブンなふるまいなので、タイミングの管理は必要経費だと思うことにしています。

 

 

 

これでテスト用のブループリントは完成です。

 

あとは適当なレベルブループリント作ったら Viewportに置いてプレイするだけです。

 

せっかくなのでデザインを変えてみました

 

 

ちょっと遅れて消えるようにしています。

ゲージの変化量と、視界の中に入りやすいかどうかで、この演出時間は加減するといいでしょう。

こういったプロトタイプを作ることで、手触りという経験値を手に入れることができるのです。実装を担当してくれるエンジニア、プログラマ?に自信をもって指定書を渡せるようになりますよ。

 

UIデザインが決まってから、オーダーを受けて作り始める場合、試行錯誤のスケジュールが組み込まれていないことがほとんどだと思います。一般にUI開発のスキルについて語られにくいのはこういった小さなマイクロインタラクションとどれだけ向き合ってきたかが定量化されていないのも理由としてあるのかなと、思ってみたり。

 

ということで今回は以上です

 

ただのゲージでも、いろいろな見せ方があるものですね。

残すところあと2本

どうなることやら

 

ではでは

素敵なゲージライフを!

 

 

 

 

 

 

 

第24回 ぷちコン 振り返り

いつもの100本ノックの前に軽く、先日のぷちコンに応募した作品について振り返ってみようかと思います。

youtu.be

 

期限いっぱいまで奮闘してたので提出してからしばらく燃え尽きてました。

第24回目のお題は「スピード」

珍しく具体的なワードだなという印象。

絵的な「速さ」の表現がブラーくらいしか思いつかなかったので、ネタ出しを諦めかけていたところにTVの気象情報にハッとなった。はるか昔 図鑑に風力の段階について書かれていて、子供心に恐怖を覚えたのを思い出し、そうかテキストでも速度を表現できるじゃないか。というところから着想を得てできたのがこれ。

ここずっとシェーダーについての記事ばかりだったので、せっかくだからUIメインでシェーダーいっぱい使ったものにして、活用例としてあげられたらと、Widgetブループリントだけで作ることを決意。

 

内容は

論理クイズというジャンルの設問に答えて、速度を上げていくというもの。

どちらを選択しても速度レベルは上がりますが、正しい方を選択すると多くレベルアップします。設問数が尽きる前にレベルが最大になればクリアです。

 

Wikipediaで速度について調べたら、速さの比較というページがあったので、速度の状況をレベル的に示す「現在速度」について、秒速の数値と説明テキストを引用させていただきました。動画に入れるべきだったのですが、慌てていて失念していたのでこの場を借りて補足とさせていただきました。

 

 

 

 

Widgetブループリント

全部で10

そのうちレイアウトを管理して動いているのが2つ。

残りは、パーツ扱い。

 

画面を構成するのがUIのみなので、なるべくいろんなものを動かすようにした。

今回 割り込みが多いので、タイムラインを使ったアニメーションは少なく、主にSetTimerByEvent を利用。

 

いくつかピックアップします。

 

ただテキストを表示しただけだと面白くないのと、視線を誘導する目的で一文字ずつ出現するWidgetBPを用意。

カスタムイベントで、文字列を受け取ったら、一文字ずつバラしてHorizontalBoxに追加。指定された時間を待ったら、出現開始というつくり。

Construct Object From Classノードで TextBlockを生成。

表示のたびに生成すると負荷になるので、事前にいくつか生成しておく。

以降はそれをクリンナップしながら再利用。

 

 

不等号の記号<>を動かしてみた。

マテリアルで作ったものだけど、キー入力に合わせてキャンセル的な動きをさせるのが

結構大変だった。

スタートとゴールを変数で持っていて、キー入力で書き換わる。

そのスタートとゴールを Lerp(線形補間)で遷移させるというつくり。

 

 

今回は 単位が重要な要素。段階によって変化する。

 この記事を書いていてテクスチャがミスってることに気づいた(汗  ↑ の画像は修正済み。

速度の数字も桁数が頻繁に変わるし、マイクロとかはアスキー文字に含まれてないし、小数点もある。最終速度の「光年」に至っては表示桁数が16桁となるので、String型で対応することに決めた。

テクスチャででっかく表示したいのもあって、速度と単位をまとめて表記するためのルールを作った。

 

でそのルールに従って手で整形すると面倒だしミスりそうだったので、Javascriptで整形する簡単なHTMLファイルを作ってブラウザで変換作業。

 

ゼロ埋めはしたくないので、前方の空白は x 、単位は a ~ h のアルファベットに置き換え。という簡単なルール。

表示する際は、一文字ずつバラしてSwitchノードで振り分け。桁ごとに反映させて表示。

Javascriptで遊んでたのが役に立った。文字列や配列の操作はこのころに覚えたので、ブループリントで活かせているのはありがたい。

 

 

数字の切り替わりのときに上下をグラデーションマスクにしたかったから、マテリアルで動かしている。

 

 

背景に流れる星も、テキスト演出同様に、事前に150個の Image をキャンバスにランダム配置。この時、サイズもランダムにしてその値を配列に保持。

あとはクイズの回答でレベルアップするごとにすべての配列の中身を1.125倍し Sizeに反映することで、全体が徐々に速度を上げつつ伸びるという仕様。

サイズと速度の伸びを別の配列にするか悩んだけど、最終的に サイズ×1.1 を移動速度にして動かしている。

 

Widgetのハイライトはこれくらいかな。

 

 

GameState

ステート、設問、速度テキストの管理

メインのWidgetブループリントの比重が重くなるので、ここで保持することにした。

設問等はTableアセットで持てばCSVが使えるのでエンジンへの組み込みは楽できたんだけど、当初はそれほど大きな配列を作る想定ではなかった。

 

 

BPインターフェイス

GameStateとのやり取り用。アクセスする際にキャストするのがちょっと重たく感じたので、インターフェイスを活用。

GameStateブループリントにセットすると、自分の作ったGameStateの中の関数にMessage経由で呼び出せるようになるので便利。

 

 

ストラクチャ(構造体)

設問一式と速度情報の表示で、まとめて扱うのでグルーピングする感じ。

いろいろ整形してから表示するので、String型をメインに構成。

これを配列にして、Index番号で適宜取り出せるように。

 

 

Enum

ゲームの状態を「ステート」名で識別するために使用。

Enumを変数管理しておいて状況によって書き換える。

今回決定ボタンやポップアップを閉じるときなどにEnterキーを使うようにしたので、状況に応じて意図的に弾いたり、遷移先を制御するのに利用。



 

テクスチャ、マテリアル、マテリアルインスタンス

マテリアルは、テクスチャアトラスからパーツを取り出すのと、カラーの統一を容易にする目的。

カラーのアニメーションも、マテリアル内に A と B の2色を持たせておいて、パラメータで切り替えるようにしたので、プロジェクト全体での色の調整箇所を少なくできる。

ParentMaterial(親マテリアル)として、汎用的に使うために用意したら、あとはマテリアルインスタンスを都度作って、UVを指定する。

各所のWidgetブループリントで散りがちなカラー管理を一括したい場合に使える手法。

タイムラインでのカラーアニメーションは何かと大変なのでおすすめ。

 

マテリアルインスタンスを使って、速度レベルアップの演出を作ってみたら、結構いい感じに動いてくれたので、次の記事 ゲージ100本ノック #98 で紹介します。

 

 

サウンド

キー入力時にSEを鳴らしたら、なんとなく UIの動きより気持ち遅れる印象だったので、UIが動く直前にマクロでDelay 0.2秒 を入れて組み込んでみたら気にならなくなった。

一部のSE および BGMは ポケットサウンド さんの音源を使用させていただきました

またキー入力とレベルアップ時に 効果音ラボ さんの音源を使用させていただきました。

ありがとうございます。

 

 

文字について

ロゴや見出しに「ぶっとくスピード感」をコンセプトに時間がかからないのを目指して自作しました。Asciiの半角カナをイメージ。全てPhotoshopのシェイプです。

下は斜体にする前の状態。

必要になるたびに追加していったので、こうやって全体を見渡すとバランスのおかしいところがいっぱい・・・

ウェイトの太い文字と普通の文字でメリハリをつけると、UIにリズムが生まれます。

 

 

最後に

いつもながら素敵な機会を用意してくださって、本当に有難い限りです。

今回はたまたま早くにネタを思いついたので着手できました。

UEで日々 UI制作 を頑張っている方たちへ、刺激物になれればいいなと思っていろいろ仕込んでみました。フンッと鼻で笑っていただけると光栄です。

今回は慣れないモデルの扱いに怯んだりすることはなかったので、ぎりぎりでしたが楽しく進めることができました。

 

ここからは告解

 

実は・・・

設問数を当初の予定通りに用意できませんでした。

本当は20問用意するはずでしたが、できたのは半分の10問。「比較」というのがネックで思ったより難航してしまいました。

途中から同じ設問が続くことになるけど ゲームの流れは完成していました。

設問数が少ないなら、少ないなりに判定結果を調整してしまうこともできたのですが、52段階用意した速度レベルをなんとか雰囲気だけでも伝えたかったのもあって、スコアの上昇率を調整したり設問の配列の並びを入れ替えて撮影しました。

動画編集はPremierRushです。切ってつなぐくらいしかやり方を知らないのですが、上記の細工と動画の尺でなんとか、同じ設問が表示されるのを回避しました。

まぁ 「できてないやんけ」 と言われれば、・・・ソウナンデスヨ、テヘペロ

と返すしかできないのです。

同じ設問が表示されるバグが残ってたと言い訳できればまだマシだったかもしれません。

応募を間に合わせたとはいえ、日に日に心苦しくなってきたのでここに書き残すことにしました。

まぁUMGだけで形にできたので悔いはないです。

 

 

 

ではでは

素敵な ぷちコン ライフを!