本文へスキップ

ビットマップフォント作成 · シートやTTFからBMFont .fnt

入り口は3つ、出てくるフォントは1つ:固定グリッドのグリフシート、インクから幅を測った同じシート、端末で描画したフォントファイル。結果は別のパーサーが同じように読めるBMFontテキストです。

ピクセルグリフを端末内で描くか、PNGシート・BDF・編集用プロジェクトを読み込めます。1枚のPNGアトラスと同じメトリクスのBMFontテキスト・XML・バージョン3バイナリを書き出します。編集用プロジェクトにはv2原本と、制限内でTrueTypeのピクセル輪郭フォントも含まれます。入力文の不足文字数とカーニングをプレビューでき、ファイルはアップロードされません。CC0のBDF 2種類の出力は独立して再読込済みですが、新しいBDF経路のエンジン読み込みは別途確認が必要です。

UIパネル・ボタン・グリフシート・UIシート(PNG)をドロップ
UIラボを開く
ブラウザ内で処理し、ファイルはアップロードされません。
UIラボ:ナインスライスのガイドと複数サイズのプレビューがあるCC0のUIパネル
CC0アセット(Kenney)を開いた実際のUIラボ画面です。
出力
  • Nine-slice測定確認· Pillow
  • Bitmap font測定確認· independent .fnt parser
  • Button states測定確認· Pillow
  • Engine import未検証

.fntファイルが1行ずつ伝えること

infoは書体名とサイズ、余白と間隔を記録します。commonにはlineHeight(テキストの1行から次の行までのピクセル)、base(行の上端からベースラインまでのピクセル)、scaleW・scaleH(テクスチャのサイズ)、pagesがあります。page id=0 file="font.png"は.fntからの相対ファイル名でテクスチャを指します。charの各行はid(10進数のUnicodeコードポイント)、x y width height(テクスチャ内のグリフの矩形)、xoffset yoffset(ペンの位置と行の上端を基準にその矩形を描く位置)、xadvance(描いた後にペンが進む距離)です。kerning first second amountの行は特定の文字の組を調整しますが、編集用プロジェクトには文字ペアのカーニングを保存できますが、画像シート自体には読み込むカーニング情報がありません。

文字列を描くとき、エンジンはペンをx = 0から始め、文字ごとに矩形を(ペン + xoffset, 行の上端 + yoffset)にコピーしてペンにxadvanceを足し、次の行はlineHeightだけ下から始めます。固定グリッドモードでは送り幅がすべてセル幅なので、等幅のピクセルフォントに向きます。実測モードはセルごとに不透明なピクセル(アルファ8超)だけを残し、オフセットで元の位置に戻し、インクの右端にグリフ間隔を足した分だけ進めます。スペースのような空のセルも送り幅を持ちます。

文字セットがアトラスの大きさを決めます。ASCIIは95文字(U+0020〜U+007E)、Latin-1は191文字で、完成形のハングルだけで11,172音節、主要なCJK統合漢字ブロックは20,992コードポイントあるため、韓国語・日本語のプリセットは貼り付けたテキストに実際に出てくる文字だけを取り出します。符号付き距離場(SDF)はテクセルごとに被覆率の代わりにグリフの縁までの距離を保存し、シェーダーが別のサイズでもくっきりした縁を切り出せるようにします。Nerulioは128を縁、spreadピクセル分の距離をバイトの範囲に割り当て、グリフごとに4倍の解像度で正確な距離を計算し、MSDFではない1チャンネルのSDFを出力します。

lineHeight
テキストの1行から次の行までのピクセル。
base
行の上端からベースラインまでのピクセル。
xoffset / yoffset
ペンと行の上端を基準に、グリフの矩形を描く位置。
xadvance
グリフを描いた後にペンが進む距離。
SDF spread
0〜255の範囲に割り当てる距離(ピクセル)。128がグリフの縁。

例:同じ3つのグリフをグリッドと実測で

A、i、スペース用の8 × 8セルを持つ24 × 8のシート、ベースライン7。ツールが書き出す行です。

common    lineHeight=8 base=7 scaleW=24 scaleH=8 pages=1
グリッド  char id=65  x=0  y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8
          char id=105 x=8  y=0 width=8 height=8 xoffset=0 yoffset=0 xadvance=8
実測      char id=65  x=1  y=1 width=5 height=6 xoffset=1 yoffset=1 xadvance=7   (1 + 5 + 間隔1)
          char id=105 x=11 y=1 width=1 height=6 xoffset=3 yoffset=1 xadvance=5   (3 + 1 + 1)
          char id=32  x=16 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=3   (空のセル:round(8 ÷ 3))
"iA i" 実測     iは0 + 3 = 3 → ペン5;Aは5 + 1 = 6 → ペン12;スペース → ペン15;iは18 → 幅20px
"iA i" グリッド グリフ4つ × 8 = 32px

実測フォントはiを8pxではなく5px間隔で置くので、プロポーショナルな文字が自然に見えます。font.jsonにも同じ値がglyphs[].xAdvance、xOffset、yOffsetとして入っています。

できること

  • 正確なUnicode座標の固定グリッド(非ラテン文字も可)、またはインク基準のx・yオフセットと送り幅を測る実測モード。
  • 端末のフォントファイル(TTF/OTF)をFontFaceで読み込み、均一グリッドに描画。送り幅はフォント自身の値。
  • 文字セット補助:テキストで使っている文字、ASCII(95)、Latin-1、テキスト内の韓国語・日本語の文字。
  • SDF(ベータ):グリフごとの正確な距離変換。MSDFとカーニングはなし、1ページ。

使い方

  1. グリフシートを入れるかフォントファイルを選ぶ。
  2. セルサイズ・ベースライン・文字を設定。
  3. 固定・実測・フォントファイルのモードを選ぶ。
  4. font.png + font.fnt + font.jsonを書き出し。

エンジンでフォントを読み込む

  1. font.fntとfont.pngは一緒に置いてください。pageの行がPNGを相対ファイル名で指しているので、名前を変えるなら両方変えます。
  2. Godot 4:2つのファイルをプロジェクトにコピーすると、Godotが.fntをビットマップフォントとしてインポートします。LabelなどControlのフォントのテーマオーバーライドに割り当てます。ビットマップフォントはデザインサイズが固定なので、ピクセルフォントはそのサイズか整数倍で描いてください。
  3. PixiJS 8:await Assets.load('font.fnt')のあとnew BitmapText({ text, style: { fontFamily: 'Nerulio Grid' } })を作ります。familyはinfo行のfaceです(Nerulio Grid、Nerulio Measured、またはNerulioとフォントファイル名)。
  4. Phaser:this.load.bitmapFont(key, 'font.png', 'font.xml')を使います。ZIPにはPNGとXML記述子が入っています。利用するPhaserのバージョンで実際の表示を確認してください。

結果の確認

  • テキストエディターでfont.fntを開きます。chars countがcharの行数と同じで、すべての矩形がscaleW × scaleHの内側にあるはずです。
  • シートの下のサンプル行はフォント自身の値で描いています。ここで重なったり離れたりする文字は、エンジンでも同じようになります。
  • 出荷前に、ローカライズファイルでこのフォントを欠落グリフチェッカーにかけてください。

トラブルシューティング

症状考えられる原因確認方法対処
エンジンがフォントを読み込むが何も表示されないfont.pngの名前を変えた、または.fntと別の場所に移したpage id=0 file=の行と実際のファイル名pageの行に書かれた名前でPNGを.fntの隣に置く
Phaserがフォントを解析できないPhaserのload.bitmapFontはBMFontのXML形式を求めるローダーがデータを解析するときのコンソールのエラー同梱のfont.xmlをfont.pngの隣に置き、Phaserのコンソールとパスを確認する
実測モードで文字がくっつく・離れすぎるグリフ間隔の値、またはアルファ8を超える薄いピクセルがインクの箱を広げている.fntで細いグリフと太いグリフのxadvanceを比べるグリフ間隔を変えるか、シートの薄いピクセルを消す
フォントファイルモード:info sizeが選んだサイズより大きい入力したフォントサイズではなく、セルの高さ(ascent + descent + 余白)を書いているinfo sizeとフォントサイズの欄を比べるエンジンがsizeを使う箇所では、描画したサイズを手で指定する
フォントファイルモード:一部の文字が別の書体に見えるTTFにない文字をブラウザーの代替フォントが描き、シートに焼き込んだグリフシートで形の違う文字を探すその文字を外すか、その文字を含むフォントファイルを選ぶ
フォントファイルモード:ピクセルフォントがぼやけるCanvasの文字はアンチエイリアスされ、縁に半透明のアルファが出るシートを拡大すると縁にグレーのピクセルピクセルフォントはシートとして描き、グリッドか実測モードを使う
グリッドモードで文字順がグリッドに収まらないと出るセルより文字が多い、同じ文字が重複している、または順序が空シートの列 × 行と文字数を比べる重複を消すか大きいシートを使う。端にあるセルより狭い帯は無視される

ほかの方法

方法こんなときに向いています
Godot自身の画像フォントのインポート(インポートの種類 Font Data (Image Font))Godotだけで使い、シートの並びが単純なとき。追加ファイルが不要で、インポートドックで文字範囲・余白・カーニングペアも指定できます。
AngelCode BMFontなどBMFont互換のジェネレーター検証済みの複数テクスチャページ、エフェクト、高度なフォント機能が必要な場合。このエディターは現在1ページのアトラスを書きます。
エンジンで動的フォント(TTFそのもの)を使う文字サイズが大きく変わる、または数千のグリフが必要な言語のとき。GodotのドキュメントはMSDF描画をビットマップフォントではなく動的フォントについて説明しています。

出力ファイルと検証方法

「測定確認」は、ダウンロードしたファイルをページ外(Pillow・numpy・独立したパーサー)で開き直して性質を測ったという意味です。ゲームエンジンでの読み込みは試していません。

書き出し先ファイル検証
Nine-slicenine-slice.json · previews · SETUP.md測定確認 Pillowリサイズした全プレビューで四隅のブロックは拡大されません。Chromiumではバイト一致、Firefoxでは半透明ピクセルがアルファの丸め1段階ぶん変わることがあります。すき間・重なり0。
Bitmap fontfont.png · font.fnt (BMFont text) · font.json測定確認 independent .fnt parserテスト内で別に書いたパーサーで.fntを読み戻し、ヘッダーと全グリフ範囲が一致。
Button statesstates/*.png · strip · states.json測定確認 Pillownormalは元と一致(Chromiumではバイト一致、Firefoxでは半透明ピクセルの色がアルファの丸め1段階以内)。states.jsonの各範囲がストリップから該当状態を正確に切り出します。
Engine importGodot 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ページで、ローカルTTF/OTFの焼き込みとBDF・プロジェクト編集は現在最大1,024文字です。複数ページのCJK出力とOTFダウンロードは未検証で、SDFはベータです。文字数が合っても合成ハングルの可読性は保証されません。
  • 編集用TTFはピクセルの四角形を輪郭にします。CC0のBDF 2種類をfontToolsで再読込しましたが、FreeTypeの標準ヒンティングではiとjの離れた点が移動しました。Unity TextMeshProでの読み込みは未検証です。
  • 従来のローカルフォント焼き込みではブラウザーの代替グリフやアンチエイリアスが混ざることがあります。対象エンジンで出力アトラスを確認してください。
  • カーニングペアなし、フォントは1ページ、MSDFなし。SDF出力はベータです。
  • TTFのcmapを解析しないため、欠落グリフのチェックには.fntかラボのフォントが必要です。
  • アトラスは1ページで、接している要素は1つとして扱います。
  • Godot・Unityでの描画は行っていません。エンジン用の値(patch margin、L・B・R・Tの境界)はドキュメント基準で、エンジンでは未検証です。

バージョンと出典

docs/UI-LAB.mdのためにChromiumで測定しました。固定グリッドのフォントは16 × 8のfont.pngで、あをchar id=12354 x=8 y=0 width=8 height=8と書き、テスト内の独立したパーサーが.fntのヘッダーと全グリフの記録をJSONと同じ値で読み戻しました。実測モードはぴったりの矩形、3種類以上の送り幅、送り幅を持つ空のセルを作り、TTFからの32pxのASCII-95アトラスは28ms(350 × 380のシート)、SDFは1174msで作られました。上のフォントファイルモードの問題は、このリリースで未修正の既知の問題です。BMFontのフィールドはAngelCodeのドキュメント、エンジンでの読み込みはGodot 4.7・PixiJS 8・PhaserのAPIドキュメントに基づきます。

参照した公式ドキュメント

よくある質問

カーニングペアは作れますか?

いいえ。ビットマップシートにはカーニング情報がなく、作り出すと誤りになります。.fntにはkernings count=0と書かれます。

GodotやUnityで読み込めますか?

BMFontテキストは一般的な形式ですが、ここではGodot・Unityでの読み込みは試していません。

無料ですか?アカウントは必要ですか?

このページの機能はすべて無料で、アカウントなしで使えます。透かしや制限された書き出しはありません。

スプライトはアップロードされますか?

いいえ。Studioはこのブラウザのタブ内でデコード・パック・書き出しを行います。プロジェクトはブラウザ(IndexedDB)に自動保存され、.nerulioファイルとして保存できます。

Asepriteやエンジンのプラグインは必要ですか?

いいえ。.asepriteファイルはブラウザで読み書きします。エンジン別のバンドルには必要なものが入っています(Godotはインポート設定とシーン、UnityとGodotのタイルセットは小さなエディタースクリプト)。