ゲームUIアセットラボ · UIシートを余白つきアトラスに
ゲームUIの絵のための作業画面:シートから要素を見つけ、余白と縁の拡張でパックし、パネルにナインスライスの境界を付け、実際に描画される解像度でチェックします。
UIシート(ボタン・パネル・アイコンを1枚の透明PNGに描いたもの)は、エンジンが描画する前に名前つきの矩形に分ける必要があります。UIラボはアルファのしきい値を超えるピクセルのかたまり(島)を要素として見つけ、統合距離より近い島(ボタンと横のバッジ)をまとめ、余白と任意の縁の拡張を入れてui-atlas.pngにパックし、各矩形とナインスライスの境界をui-atlas.jsonに書き出します。書くのは数値で、GodotやUnityのリソースファイルではありません。9スライス・状態・フォント・チェックの各ステージは同じ画像で続けて使えます。

- Nine-slice測定確認· Pillow
- Bitmap font測定確認· independent .fnt parser
- Button states測定確認· Pillow
- Engine import未検証
UIシートからアトラスの矩形へ
要素は手で輪郭をなぞるのと同じ考え方で見つけます。アルファがしきい値(既定は255中8)を超えるピクセルを8方向の隣とつないで島にし、最小サイズ(既定16ピクセル)未満の島はゴミとして捨てます。
島は候補にすぎず、意味はわかりません。アイコンの横に離れた点があれば島は2つです。統合距離(既定4px)は、すき間がその値以下の箱どうしを合わせ、合わせるものがなくなるまで繰り返すので、近くに並んだ部品はmergedタグつきの1要素になります。接している2つのボタンは最初から1つの島なので、統合距離に関係なく1要素のままです。
パックはMaxRectsで要素を最大4096 × 4096pxの1ページに置き、周りに余白(既定2px)を空けます。縁の拡張は要素のいちばん外側のピクセルをN px外へ複製し、フィルタリングやミップマップが要素のすぐ外をサンプリングしても要素自身の色を読むようにします。JSONの矩形は要素そのもので、拡張した縁はその外側にあります。
- アルファの島
- アルファのしきい値を超え、8方向につながったピクセルのまとまり。
- 統合距離
- 2つの箱を1要素にまとめる最大のすき間(ピクセル)。
- 余白(padding)
- パックした要素の間に空けるピクセル。
- 縁の拡張(extrude)
- 要素の矩形の外側に並べる、縁のピクセルのコピー。
例:256 × 128のシート1枚を実測
透明なシート上の図形5つを、ラボと同じ検出・統合・パックのコードで処理した結果です。
シート 256 × 128 アルファしきい値 8 最小 16px 統合距離 4
島 パネル 64×64 @8,8 ボタン 96×32 @96,8 アイコン 16×16 @200,12 点 4×4 @218,12 矢印 20×20 @96,64
アイコン → 点のすき間 = 218 − (200 + 16) = 2px ≤ 4 → 1要素 22×16 @200,12、タグ "merged"
結果 島5つ → 要素4つ(統合距離0なら5つのまま)
パック 余白 2、拡張 1 → ui-atlas.png 102 × 108
矩形 element-02 3,3 96×32 element-01 3,41 64×64 element-03 73,41 22×16 element-04 73,63 20×20要素の番号はパック前に上から下、左から右の順に付きます。element-01がパネル、element-03が統合されたアイコンです。名前はJSONのframesのキーになるので、書き出す前に変えておきましょう。
できること
- 統合距離つきのアルファの島で要素を検出(ボタンとその上の文字が1要素に)。
- UIアトラス:余白・縁の拡張つきMaxRectsパック。要素ごとの境界がJSONに入ります。
- ナインスライス・ボタン状態・ビットマップフォント・チェック(サイズとアンカー・セーフエリア・ローカライズのあふれ・WCAGコントラスト)の各ステージ。
- 設定リンク:画像以外のすべての設定をURLで共有可能。
使い方
- UIシートをドロップ。
- 検出された要素を確認し、名前を変え、統合距離を調整。
- パネルにナインスライスの境界を付ける。
- アトラスPNG + JSONを書き出し。
結果の確認
ui-atlas.jsonを開きます。framesには残した要素ごとに1項目あり、すべてのrectがmeta.sizeの内側にあるはずです。- 任意の画像エディターで
ui-atlas.pngからrectを1つ切り出し、元シートの同じ要素と比べます。同じピクセルのはずです。 "tag": "merged"を探します。どれも意図してまとめた部品であるべきで、たまたま近くにあった2つのボタンであってはいけません。- 要素にナインスライスの境界を付けた場合、
SETUP.mdにその名前の節と4つの数値があります。
トラブルシューティング
| 症状 | 考えられる原因 | 確認方法 | 対処 |
|---|---|---|---|
| 2つのボタンが1要素になる | 接している、またはすき間が統合距離以下 | 要素の箱が両方のボタンを覆い、島が複数ならタグがmerged | 統合距離を0に。接しているなら絵の上で透明ピクセルを1列以上空ける |
| 文字・光・影が別の要素になる | ボタンとの距離が統合距離より遠い、またはピクセルがアルファのしきい値より薄い | 期待する要素数と比べ、すき間を拡大して見る | 統合距離を数px上げる。柔らかい影が切れるならアルファのしきい値を下げる |
| 小さな点が要素として拾われる | 散らばったピクセルが最小サイズを超える島になっている | 要素一覧にごく小さい矩形がある | 最小ピクセル数を上げるか、絵のゴミを消す |
| エンジンで要素の縁に隣の要素の線が出る | エンジンが矩形の境界をまたいでフィルタリング・ミップマップし、余白0のため隣を読んでいる | 実行画面で縁を拡大する | 余白2と拡張1〜2を使う。ピクセルアートならNearestフィルター |
| アトラスのパックに失敗する | 要素が4096 × 4096の1ページに収まらない | 概要にサイズではなくエラーが出る | シートを分けてアトラスを2つ作る。複数ページのUIアトラスはない |
ほかの方法
| 方法 | こんなときに向いています |
|---|---|
| 個別PNG向けのパッカー(スプライトシートパッカー、TexturePacker) | 要素がすでに1ファイルずつあるとき。検出は不要で、エンジン別のアトラス形式を書き出せます。TexturePackerなしでパックするも参照。 |
| UnityのSprite Editorでのスライス | シートをUnityだけで使い、矩形をUnityのインポート設定に保存したいとき。 |
GodotのAtlasTextureを手で作る | 要素が2〜3個だけのとき。atlasにシートを指定してregionを入力すれば、追加ファイルは一切不要です。 |
出力ファイルと検証方法
「測定確認」は、ダウンロードしたファイルをページ外(Pillow・numpy・独立したパーサー)で開き直して性質を測ったという意味です。ゲームエンジンでの読み込みは試していません。
| 書き出し先 | ファイル | 検証 |
|---|---|---|
| Nine-slice | nine-slice.json · previews · SETUP.md | 測定確認 Pillowリサイズした全プレビューで四隅のブロックは拡大されません。Chromiumではバイト一致、Firefoxでは半透明ピクセルがアルファの丸め1段階ぶん変わることがあります。すき間・重なり0。 |
| Bitmap font | font.png · font.fnt (BMFont text) · font.json | 測定確認 independent .fnt parserテスト内で別に書いたパーサーで.fntを読み戻し、ヘッダーと全グリフ範囲が一致。 |
| Button states | states/*.png · strip · states.json | 測定確認 Pillownormalは元と一致(Chromiumではバイト一致、Firefoxでは半透明ピクセルの色がアルファの丸め1段階以内)。states.jsonの各範囲がストリップから該当状態を正確に切り出します。 |
| Engine import | Godot NinePatchRect · Unity Sprite border · BMFont import | 未検証Godot・Unityでは実行していません。 |
検証の根拠
ナインスライスの四隅が100×40・300×80・800×200・420×120で拡大されず、同一列12本から境界6·6·6·6を提案。ボタン状態:normalは元と一致、disabledはアルファ128のグレー、focusリングも正確。.fntのグリフ記録がJSONと一致。.poの欠落グリフを正確に列挙。白地に黒文字で21:1を測定。画像はブラウザのCanvasを通ります。Chromiumはバイトがそのまま、Firefoxは半透明ピクセルの色を乗算済みの値で1段階に丸めます(Kenneyのボタンで測定:アルファ63で最大4段階)。アルファと不透明ピクセルはそのままです。
制限
- アトラスは1ページ、最大4096 × 4096で、回転してのパックはありません。
- エンジンのリソースファイル(Godotの
.tres、Unityの.meta)は書かず、矩形と境界の数値だけを書きます。 - カーニングペアなし、フォントは1ページ、MSDFなし。SDF出力はベータです。
- TTFのcmapを解析しないため、欠落グリフのチェックには.fntかラボのフォントが必要です。
- アトラスは1ページで、接している要素は1つとして扱います。
- Godot・Unityでの描画は行っていません。エンジン用の値(patch margin、L・B・R・Tの境界)はドキュメント基準で、エンジンでは未検証です。
バージョンと出典
docs/UI-LAB.mdのためにWindows 11のChromium(Playwright)で測定しました。ボタン1つと透明な穴がある128 × 64のシートから要素が4つ見つかり、統合距離40で1つにまとまりました。パックしたピクセルは元の矩形と一致し、拡張1は縁のピクセルを矩形の外に複製します。要素に付けた境界5/5/4/4はui-atlas.jsonに入ります。要素40個の1024 × 1024シートは検出・統合・パック・描画が76msで終わりました。GodotやUnityでアトラスを読み込んではいません。上のエンジン名はGodot 4.7の公式ドキュメントに基づきます。
参照した公式ドキュメント
よくある質問
接している2つのボタンが1要素になるのはなぜ?
要素はアルファの島なので、接している絵は1つの島です。絵の上で離すか、ナインスライスのステージで切り出してください。
GodotやUnityのリソースを書き出しますか?
いいえ。エンジンごとに必要な値(patch margin、L・B・R・Tの境界)入りのPNG + JSONとSETUP.mdを書き出します。エンジンでの読み込みは試していません。
無料ですか?アカウントは必要ですか?
このページの機能はすべて無料で、アカウントなしで使えます。透かしや制限された書き出しはありません。
スプライトはアップロードされますか?
いいえ。Studioはこのブラウザのタブ内でデコード・パック・書き出しを行います。プロジェクトはブラウザ(IndexedDB)に自動保存され、.nerulioファイルとして保存できます。
Asepriteやエンジンのプラグインは必要ですか?
いいえ。.asepriteファイルはブラウザで読み書きします。エンジン別のバンドルには必要なものが入っています(Godotはインポート設定とシーン、UnityとGodotのタイルセットは小さなエディタースクリプト)。