ずいぶん記事更新をサボってしまいました。
最近セカンダリ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 X に 90 を入力します。

※サイドパネルが出ていない場合は [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)が並んでいる状態です。
例えば V は Y でもあるし 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ライフを!