みつまめ杏仁

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

角丸ボックスおまけ

 UI表示をメッシュで作る理由はいくつかあって、特に重要なのが無駄なピクセルフィルを減らすことと、テクスチャの節約。あとは頂点情報を生かした多様な表現が可能とか、テクスチャの解像度変更が簡単(アスペクト比が同じ場合)などなど。細かくなりそうなので、また気が向いたら記事にしていこうと思います。

 

 さてさて、解像度が高いとテクスチャ容量がネックになるため、なんとかやりくりしないといけません。そこで切り詰めて節約したデザインパーツをパズルのピースのようにポリゴンで再構築していくわけですが、UVが素直に並ぶことは 滅多に無いので工夫が必要になります。

 この前の記事で作った角丸ボックスはフチドリの表現をいくつか載せただけなので、今回はボックス全面に柄を入れる方法について書こうと思います。

 

limesode.hatenablog.com

 角を丸くするためのUVと、サイズを変えるために頂点を動かすためのUV、2つの UVを備えたメッシュです。

 

できたものはこんなやつ。

アニメ「モノノ怪」をご存じの方はイメージしやすいと思います。

Photoshopだとクリッピングマスクが近いでしょうか。

これはメッシュを画面に描画する際、先に頂点の位置を決める計算が行われるので、その決定後の頂点位置を画面全体(描画領域)を 0~1としたUV座標に変換して利用することで、メッシュの形状に絵が同期しない状態にできます。

 

そこで使用するノードがこれ。

 

ViewportUV はアプリが描画してる領域が対象です。

 

描画するメッシュの各頂点位置をこの 0~1 の範囲に計算してくれます。

そのUVを使って絵を切り出すということは、前回の記事のメッシュだとポリゴンのUVが実質3つあるのと同じことになります。


結構使える場面があるのですが、少々厄介な問題がモニタのアスペクト比です。

 

PCの場合はフルスクリーンモードとウィンドウモードの2つがあります。

家庭用のTVはアスペクト比が基本的に16:9ですが、PC用のモニターは16:10とか32:9とか、規格がいっぱいあって、2D基準でレイアウトするGUI にとっては頭痛の種。

ウィンドウモードはアプリ側でアスペクト比を固定できるのでいいのだけれど、フルスクリーンにするとモニタのアスペクト比に強制的に合わせられてしまう。

 

そこで アスペクト比の差ででテクスチャが変形して見えるのを防ぐために、アスペクト比を補正するのに使えるのがこのノード。

表示および表示用バッファの解像度を取得できるノードで、試しにデバッグ表示してみると、若干のブレみたいなのがあるけど、これでアスペクト比が計算できます。

基本は Visible Resolution の方で大丈夫だと思います。ピンのタイプは Vector2(Float2)なので、Component Mask ノードで分離させて割り算します。

例えば 1920 を 1080 で割ると 1.77777777・・・ になります。

タテを 1 とした時の ヨコの倍率です。

もう一方 1080 を 1920 で割ると 0.5625 で、ヨコを 1 とした時の タテの倍率です。

これらの補正値を、見た目の伸びを解消するために使います。

 

試しに New Editor Window (PIE) で起動してサイズを変えてみた。

カメラの画角(FOV)が変わったのでメッシュの大きさが変わりましたが、テクスチャのタテとヨコの比率は同じです。

補正をかけないとこうなります↓

 

ScreenPositionノードは、描画した位置に合わせてくれるので、うまく使いこなすとテクスチャの節約もできてお得です。

 

 

ところで「テクスチャ」と書きましたが、シェーダーで動きをつけています。

参考までに、斜めに交互にスクロールするシェーダーを載せておきます。

下図はカラーの部分になります。それ以外の部分は前回の記事で紹介しているので省略します。

 

なにやらいろいろ計算していますが、この部分でタイリング回数の設定と、奇数ラインと偶数ラインと逆方向に移動させています。

 

テクスチャはこれ  2048 x 128 のグレースケール。

シェーダーでタイリングしています。

第三のUV であるところの ScreenPosition をうまく使えば、メッシュで面白い表現や節約ができるので、いろいろ作って試してみてほしいですね。

 

ちなみに、これをやろうと思ったきっかけが NTE (Neverness To Everness)

 スマホ操作を想定すると、どうしてもタッチ部分の情報量が多くなりがち。わりと解像度の高そうなテキスト表現があちこちにあって、テクスチャが使われてるとしたら、それなりにやりくりしてるはずで、なかなかのテクニシャンだなと想像しています。デザイナーとTAやエンジニアで分業化が進んでたりすると、こういったハイカロリーなUIを作るのは、結構コストが高くつくと思うんだけど、それなりにいい環境で製作されているんだろうなと思う。UIアーティストの職域も気になるけど。

 

 NSW2のトモコレとか見ていても、直線を排したデザインで、結構大きめのシェイプをゆったりと動かしています。地味だけど簡単にはできなさそうなテクニックを任天堂は実装してくるので興味が尽きません。ピクセルの伸びが見られないし、おそらくポリゴンを計算で描いてるか、メッシュの頂点アニメーションか。どちらかだと思いたい。テクスチャであのサイズは容量がもったいなすぎる。ポリゴンじゃないとすればベクターシェイプかな。ベクターシェイプはシェーダーで使えないと、テクスチャを載せられないから、フォントみたいにオフスクリーンのバッファに描いてる???何か特別なライブラリとパイプラインでもあるのか???とか気いろいろ妄想している今日この頃ですが、みなさまいかがお過ごしですか?

 そろそろ気温の高い季節に移り変わり、PCの熱も相まって室温がすぐに上がるのがツライですが、アイスコーヒーの氷を鳴らしながら頑張ります。

 

 冒頭で書いたように、メッシュで作る UI は手数が必要で手軽というわけではないですが、スプライトで構成するより、少ないテクスチャで高解像度かつピクセルフィルの少ない表示が可能なので、高解像度時代をうまく乗りこなす技術としてもっと当たり前になってほしいな。

 

ではでは

素敵なメッシュUIライフを!

 

 

 

 

メッシュで角丸ボックスをつくろう

ずいぶん記事更新をサボってしまいました。

最近セカンダリUVをいろいろ試してて、これはというのができたので記事にすることにしました。2026年になって、UIで平面のローポリを扱った記事を書いているのは自分くらいでは?と思う。ちょっと前にVFX界隈で記事を見かけたくらい。

2DベースのGUIエディタが充実している現状では、メッシュに手を出そうというUIデザイナーはなかなか現れないのではないかと思うんだけど、どうなんでしょうね。

 

GUI専用エディタがなかった時代を生き抜いてきた身としては、「ポリゴンUI面白いよ」と叫びたいところですが、モダンを解さないロートル枠に放り込まれる恐怖と戦う日々を過ごしつつ、なんとか啓蒙できないかと策を練っているところです。

 

最近だと、HoYoverse系や、エンドフィールド、 NTE をプレイしてみると、UI画面デザインの奔放さが伝わってきます。ほんとよく動いてる。技術的にもすごく研究されてて、実装の面倒な表現をいくつも見せられると、思わず嫉妬しそうになったりする。

ただゲームパッドでUIを操作しづらい設計なのは スマホ対応を考えるとわからなくはないけど、ちょっと残念に思う今日この頃。

 

さてさて

メッシュを使ったスケール9グリッドとか 9スライスグリッドとか呼び方はいろいろあるようです。これをメッシュで作りつつ、4隅の角丸をシェーダーで処理します。

矩形で作る角丸では、4隅に無駄な透明部分ができてしまいます。それを回避するのがメッシュを使った角丸です。

これ一つ作っておけば、いろんなシーン、特にデザインが確定するまでに汎用的に利用できると思う。

UMGに普通に実装されているので、それでええやんと言われそうですが、前回の記事と同じように、Blenderの操作をなるべく飛ばさないように載せているので、初めてメッシュを触るUIデザイナー向けの入門用として見てもらえたら嬉しいです。

 

Blender は 4.2 を使いました。基本的な機能しか使わないので、バージョン違いによる差分はほとんどないと思います。またスクショは英語版ですが、アイコンがあるのでそれほど迷わないと思い・・・たい。

操作に慣れたら、メッシュのエクスポートまで 10分もかからないです。

 

メッシュを追加

Object Mode で

Add > Mesh > Circle

ビューポート左下に、小さな黒い■が出現するのでクリックして開きます

これは「最後の操作を調整」できるパネルです。気づきにくいですがここからカスタマイズします。見当たらない場合は [F9]キーを押すと復活しますが、直前の操作に基づくので、メッシュの追加など、Add 操作をしたらすぐにチェックするようにします。

 

開くと以下のような内容になっています。

調整する箇所は3つ

  • Vertices(頂点数)
  • Radius(半径)
  • Fill Type(面を埋める方法)

 

ゲームはリアルタイムレンダリングの世界なので、頂点の数はなるべく少ないに越したことはありません。デフォルトの 32 を 12 に変更します。

半径はメッシュの大きさになります。

制作している環境やプロジェクトでスケールの基準が決められるていれば、それに従います。

今回はUEのサンプルプロジェクトで扱いやすいサイズで作りたかったので、10m にしました。

テクスチャを基準に作り始めるタイプと違ってサイズは後からいくらでも変更できるので 1m のままでもOK。

円形の「メッシュ」としつつも、デフォルトで「面」が存在しないので、Fill Type を Triangle Fan(三角形分割)に変更します。

 

 

面の向きは、XY面で作られるので、+Z方向を向いています。

これもプロジェクトのルールに則って進めていくことになりますが、あまり設定をいじらずにエンジンに持っていったときにカメラのほうに向けておきたいので、面の向きを変えます。

テンキーの[1]を押して XZ面を作業用にします。

真横から見るのでぺったんこです。

 

X軸(赤いライン)で90度回します。

ObjectMode のまま、サイドパネルから Rotation X90 を入力します。

※サイドパネルが出ていない場合は [N]キー

これで 面 ががこちらを向きます。

この向きで固定したいので Apply(適用)します。

Obejct > Apply > Rotation

面の向きはそのままに、Rotation X に入っていた 90° が 0 になります。

 

その下に Dimensions (寸法)というのがあります。

Y にオブジェクトの回転で精度の限界みたいなものが現れています。

Rotation X を Applyした後に、Scale Y を ゼロにすると完全に潰せます。

このScale も Obejct > Apply > Scale しておきます。

 

実は円を作るときに、頂点数、半径、埋め方と、一緒に Rotation X も設定可能です。

最初から XZ面に向けておくことができますが、Apply(適用)の操作は必要です。

 

 

ここからは ビューをワイヤーフレームに切り替えて進めます。

 

ついでに、オブジェクトの情報を常に確認できるようにします。

Statistics を有効にすると

ビューポートの左上に以下のような表示が追加されます。

これでうっかり操作で 頂点 や 面 を増やしてしまっても気づきやすくなるのでオススメです。

 

 

4分割する

モードを Edit Mode(編集モード)に変えます

続けて 編集対象を に変更します。[2]キー(テンキーじゃない方)で切り替えできます。

 

4本のエッジ(下図の黄色いライン )を選択します。
[Shift]キー押しながら4か所をドラッグ

ちなみに数字は 選択中 / 全体 となっています。

エッジを選んだ状態で、 Mesh > Split(分割) > Faces by Edges(選択辺で)

 

分割されると、隣り合わせのポリゴン間でシェアしてた頂点が、別々になります。

 

 

いったんスキマをつくって面を張る

編集対象を に切り替えます[3]キー(テンキーじゃない方)で切り替えできます。

 

オブジェクトの半分を選択。面の中心にある ・ をドラッグ範囲に入れると選択可能。

[G]キーを押して、マウスの中ボタンドラッグで、右方向に適当に動かします。

中ボタンを使うと、水平垂直に拘束して移動できます。

 

指を離すとビューポート左下に、調整パネルが現れます。

右に動かしたので、Move X に 1 を入力して [Enter] キーを押して確定します。

単位をつけずに[Enter] キーを押すと、勝手に m(メートル)が付けられます。

 

続けて 上半分を選択して同じように移動します。

 

編集対象を 頂点 に切り替えます[1]キー(テンキーじゃない方)で切り替えできます。

スキマを埋めるための 4つの頂点を選択して

[F]キーを押すと 面が張られます。

 

4頂点ごとに全部で5か所、ひとつづつ張っていきます。

この状態で、ビューポートでの見た目以上の数になっていると、どこかで余計な操作が入った可能性があります。

 

また、なぜか中央から張ると、中央だけ面が裏向きになるようです。

裏向きになった面を選択して

Mesh > Normals > Flip

で向きを反転することができます。

 

 

UVをあてる

頂点選択モードのまま、すべての頂点を選択します

 

エディタのタイプを UV Editing (UV編集)に切り替えます

ビューポートが左右に分かれます。

右側は、普通の3Dのビューなんですが、切り替える前のビューポートとは設定が別なので、改めてテンキーの[1]を押して平面にします。

右側のビューにあるヘッダーメニューから

UV > Cube Projection(キューブ投影)を選択  

[U]キーを押すとコンテキストメニューが現れるのでここからでも選択できます。

 

選択すると左のビューが変化します。

このマッピング方法は、立方体の6面、6方向から見える頂点をUV座標に展開してくれます。

平面のメッシュは、歪みもなく1面しかないのでシンプルな状態で展開してくれるのですごく重宝します。

最近のこういったモデリングツールは立体物の制作があたりまえなので、メッシュの形状を考慮して賢く展開するように進化しています。立体を平面にする際にどうしても歪みが出また、テクスチャを効率よく利用するように配置してくれたりします。

UI で利用する分には、歪みは敵です。勝手な位置に置かれると、シェーダーでも安定した動きにならなくなります。

 

手でUVを調整する場合は、Cube Projection じゃなくても問題ないです。

頂点の位置関係を崩したくないときにはオススメです。

 

次にUVを編集していきます。

最初からメッシュの頂点に付随するデフォルトのUV情報が プライマリ(主)、これで足りないときに追加されるUV情報を セカンダリ(副) と命名して進めます。

プロダクションによっては別の命名があるかもしれません。

 

 

プライマリUVを編集

スナップ機能の設定を変更します

UVエディターのビューで、ヘッダーにある U型磁石のアイコンでスナップのON/OFFを切り替えます。すぐ横のプルダウンメニューでスナップ先を Vertex(頂点)にします。

 

このスナップ機能を使って最終的にこの形にします。

 

操作方法は

頂点を一つ選んで、[G]キーを押します。

マウスボタンに触らずにマウスを動かし、ターゲットの頂点に近づけていきます。

ある程度近づくと吸い付くので、そのタイミングで クリックして確定します。

折り紙のようにして頂点同士を重ねるイメージ。

ただ途中でスナップ先が分かりにくくなるので慎重にひとつづつ丁寧にスナップしてゆきます。

 

サイズが小さいので最大化します。

準備として

UV > Constrain to Image Bounds (画像内に拘束?)を有効にします。

有効になっていると、UV頂点が 0~1 の範囲から出られなくなります。

何が便利かというと、マウスで適当にドラッグしても端でピタッと止まってくれるからです。

マウスで精密な動きは難しいので重宝します。

 

BlenderやDCCツールは OpenGL系のライブラリが使われているのか、UV座標は、左下がゼロ原点なことが多いです。

DirectXがベースになっているゲーム開発では、基本的に左上がゼロ原点です。

左上を基準にしたいので、2Dカーソルを左上に移します。

[N]キーをおしてサイドパネルを表示。

Viewタブに切り替えると 2Dカーソルの位置が設定できます。

Location Y を 1.0 にします。

 

スナップをOFFにして、そのすぐ左のプルダウンから 2D Cursor を選択します。

 

[S]キーを押して、マウスを動かすと左上を基準にしてスケーリングできます。

目いっぱい広がると止まるので、ここでクリックして確定します。

 

プライマリUVの編集はここまでです。

 

 

セカンダリUVを追加

Blenderのウィンドウで 右にあるプロパティパネルから緑のに切り替えて、UV Mapsを開きます。

右端にある + ボタンをクリックするとセカンダリUVが生成されます。

最初からリストにあるのがプライマリUVです。

追加するときに、このプライマリUVが複製される仕様なので、追加した UVMap.001 を選んだ状態で、改めて Cube Projection を実行します。

 

UVエディタで作業します。

変形の基準点を 2Dカーソルから元に戻します。

頂点の1/4を選んで スケールをゼロにします。

いくらか適当に小さくしたら、クリックして確定します。

ビューポート左下に、調整メニューが現れるので、開きます。

XYZに 0 を入力します。

 

残りも同じようにスケールを潰します。

これを移動させます。

マウスで動かすには Constrain to Image Bounds を無効に戻さないといけないのと、1.00000みたいな綺麗な数値にするのはほぼ無理です。

ここはむしろ数値入力の方が確実です。

まず動かしたい頂点のまとまりを選んで、2D カーソルのときと同じように、数値入力で動かします。Imageタブに切り替えて入力します。

 

縦方向は上下をさかさまにする必要があるので慎重に選択して移動します。

 

UVエディタの右上にあるプルダウンからプライマリとセカンダリを切り替えられることができます。

 

これで UV編集は完了です。

 

 

中心位置をずらす

エクスポートに向けて最後にモデルを整えます。

3Dのビューポートで作業します。

 

まず 移動の手数を惜しんだので、モデルの中心点がずれています。中央の正方形の真ん中が理想です。

 

頂点選択モードで中央の 4頂点 を選択します。

[Shift]キーを押しながら[S]キーを押して コマンドメニューを表示します。

そのままマウスを動かし、Cursor to Selected を選択します。

(何もないところをクリックすると閉じます)

これで、選択している「モノ」の中央に3Dカーソルが移動します。

 

オブジェクトモードに切り替えて

Object > Set Origin > Origin to 3D Cursor を選択します。

この操作で、メッシュの中心が3Dカーソルの位置に強制的に移動します。

 

サイドパネルを見てみると、0.5m 移動したことになっています。

Location XとZに 0 を入力します。

これでモデルの中心がワールドのゼロ原点と同じ位置になります。

 

 

頂点を戻してスキマを小さくする

面を張る作業のためにいくらか頂点を動かしているのを戻します。

このメッシュは、面積をゼロにしてエクスポートしたいのですが、UEで描画する方法を見つけられていないので、可能な限りゼロに近づけます。

ポリゴン選択モードにしてスキマを作った時と同じ方法で、中心に向かって移動します。

上下左右 半分ずつの面を選択しながら、X方向とZ方向にそれぞれプラスとマイナス、計4回。

最初に1mのスキマを開けています。モデルの中心を動かしたので 0.5m

思い切って スキマを 1mm にしたいので、0.5m から 0.5mm を引くと 0.4995m

Blender は 単位を一緒に入力すると対応してくれます

この場合、 -499.5mm と入力してもOK。

 

 

実は、中心を動かす前に、0.999m スキマを小さくしたほうが、縦と横の移動が2回で済むのですが、中心を動かす操作を説明しやすくするために、スキマがある程度広い状態で中心を動かしました。頂点同士が近く選択範囲が確認しにくくなるのを回避したかったのも理由の一つです。結果オーライなので、最終的に問題なければOK。

 

これでメッシュは完成です。

この辺で作業を保存しておきます。

File > Save

 

 

FBXエクスポート

ObjectModeでモデルを選択します

 

File > Export > FBX

を選ぶと、書き出しダイアログが出てきます。

 

設定は以下のとおり

 

Scale 0.01 は シーン設定の Unit Scaleと関連しています。

今回はデフォルトのままで作業を開始したので、Unit Scale は 1.0倍

Length は Meters(メートル) です。

円形のメッシュ Circle の半径を 10m で作ったので、そのままエンジンに持っていくと大きすぎるので、FBXの書き出し時に 1/100 にしました。

初期設定は、時間が経つと忘れがちで、新しくシーンを作って作業を開始したら、あれ?てなことになりやすいです。なのであえて初期設定は触らず 書き出し設定で帳尻を合わすことにしました。

ゲーム内の人間キャラやプロップは実スケールに合わせるのが普通ですが、UIはそもそも実在しないものだし、99.99%のデザインが半透明だし、表示サイズを優先すると、カメラからの距離に応じてサイズが変わることもしょっちゅうです。

プロジェクトチーム内でルールを作るのは前提ですが、作りやすいサイズで作って問題ないです。また、Pythonスクリプトで書き出し時のチェックや整形もできるので、そういった環境を整えれば、結構ワークフローも安定します。

 

書き出すファイル名には fbx という拡張子が付きます。

設定出来たら、書き出し先のパスを確認して、青色の Export FBX ボタンをクリックしたら書き出されます。

この書き出し設定は、Blenderを落とすと初期化されてしまうので、Operator Presets と書かれた右にある + ボタンから プリセットとして保存できます。

次回からは、このプルダウンリストから選ぶと設定が読み込まれます。

 

 

エンジンにインポート

コンテンツブラウザで右クリックしてインポートします。

初めてのインポート時はダイアログが出てきます。

StaticMesh としてインポートするので、Animation や Collision は利用しない想定です。

FBXで書き出していない情報については、それなりに忖度はしてくれるようなので

 

インポートが成功すると画面右下に以下の通知が表示され

コンテンツブラウザにアセットのアイコンが現れます。

 

マテリアルを作る

専用のマテリアルを用意します。

Blend Mode はアルファチャンネルを扱うので、TranslucentGreyTransmittance にします。さらにUI表示は光源計算が基本的には不要なので Shading Model は Unlit にします。

 

まずは、カラーとアルファをつないでいきます。


普通に Length ノードをEmissive Colorにつなぐと、

Length ノードは UV座標のゼロ原点(左上)を基準に距離を数値化するので下図のようになります。



これを、アルファチャンネルで使えるように SmoothStep でいい感じにします。

SmoothStep の Min と Max の値が調整のキモ。

SmoothStep では ギリギリを攻めています。MinとMaxの差が小さすぎるとジャギるので注意。大きすぎてもボケるので加減が難しいですが、アンチエイリアスと同じ効果が期待できます。

 

 

 

 

次に、セカンダリUVを使って頂点を動かす部分。

 

UVはVectorが2本。メッシュの頂点に適用するには、Vector を 3本にする必要があります。また、UEはカメラの奥行き方向が Y軸 なので、UV を XY にしてしまうと動きがおかしくなります。

そこで MakeVector3 ノードで組みかえます。

ちなみに、シェーダーでは 

UV XYZ RGBA 便宜上の役割を示すアルファベットを振ってあるだけで、中身は単なる Vector(Float)が並んでいる状態です。

例えば VY でもあるし G でもあります。 大事なのは順番と全体の数です。

 

セカンダリのUVは、四隅に ±1 のUV座標を割り当てています。

それにいくらかの値を掛けることで、プラス方向マイナス方向の「移動量」になります。あくまでも移動量なので、メッシュの頂点座標に足してようやく新しい頂点位置が決まって、 TransformPosition ノードで World空間に反映します。 

頂点位置は Loca Position (スケールも何も補正のない素の頂点座標)に対して単純加算しているため、そのままだと位置がおかしくなっています。それを補正するために WorldPosition(Absolute WorldPosition)を引き算して完了です。

 

これで自由なサイズの角丸ボックスが作れるようになりました。

 

角の丸みについては、メッシュのスケールで調整できます。

 

パラメータで外からサイズを変更できるように改造します。

Width と Height でボックスの縦横比を決めます。

 

Width と Height が 0だと丸の状態。

下図は スケールとパラメータの調整例

スケールが大きいほど丸くなり、全体に大きくなるぶん頂点の移動量が少なくなります。

 

これをアニメーションさせることができます。

サイズを Lerp(LinearInterpolate: 線形補間)で遷移するようにします。


※パラメータ Transform(SalarParameter) の初期値は 0 が正しいのですが、大きさのアタリを見るために 1.0で保存して進めます。

 

 

実際に動かしてみる

新しくブループリント を用意します。

作った角丸のスタティックメッシュ と Boxコリジョンをコンポーネントとして追加。

 

スタティックメッシュを追加したら、Detailsパネルで、メッシュとマテリアルのアセットをセットします。

 

 

参考までに、メッシュのScale は 2.0。


マテリアルのパラメータ  Width と Height についてはシェーダー内で 80 と 36 にした状態で保存したものをそのまま使っています。

ブループリントで動的に上書きしたり、マテリアルインスタンスでバリエーションを事前に用意しておいたり、といった利用方法があります。

 

EventGraph では、まず マテリアルパラメータにアクセスするために変数化して、あとは開閉とアニメーションの状態を見るフラグを2つ準備。

 

カスタムイベントを用意

タイムラインノードの中身は

0 ~ 1.0 で遷移させるだけの簡単なカーブです。

 

あとはBoxコリジョンのイベントからこのカスタムイベントを呼び出すようにすれば完成。

Componentsパネルの ヒエラルキーにある Box で右クリックすると、イベントの追加ができます。必要なのは2つ。

Add OnComponentBeginOverLap

Add OnComponentEndOverLap

選択すると、イベントグラフにノードが追加されるので、フラグの切り替えとカラーの変更をして用意したカスタムイベントを呼び出します。

 

コンパイルして保存したら、コンテンツブラウザからレベルにドロップして配置できます。


再生してみたのが以下

 

マテリアルのパラメータ Width と Height に事前に値をセットしておくと、任意のサイズで開閉できるので、かなり汎用的に利用できるアセットになります。

 

 

見た目を工夫

ただフラットな見た目なので、ひとまず枠線を作って安さを軽減。

 

SmoothStep ノードで少し小さな半径を作って、Lerpノードで色分け。

 

内側の SmoothStep の Min とMaxを調整してぼかしてみる。

こういったもの可能。

 

 

ここまでだと、UMGの Rounded Box と大して変わらんやん。というお叱りが聞こえてきそうなので、テクスチャを使ったアレンジを紹介しておきます。

 

下のような、128 x 4px の32bpp テクスチャで、

こういった表現が可能です。

Length の値を、テクスチャの U座標に利用しているので、SmoothStep を使わず、テクスチャのピクセルの配置だけでアレンジができます。

テクスチャは表示解像度の影響を受けるので、テクスチャの解像度もいい感じに調整する必要があります。

 

ウィンドウデザインに困ったら、ひとまずこれを表示しておけば、安っぽさは回避できるのではと思います。

 

ぜひいろいろアレンジを試してみてください。

長くなったので ScreenPositionについては次回の記事にしようかと思っています。

 

今回は以上です。

分からないところや、ご指摘などあれば このブログのコメント機能か、X にてレスいただければ対応します。

 

ではでは

素敵なローポリ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 が比較的万能感があるんですが、書き出しやプレビューの環境が簡単には揃わないのが残念です。