2013/10/05(土) [n年前の日記]
#1 [unity] Unity上でCustom Fontの設定作業が自動化できた
(※ 2013/10/07追記。色々修正した版を
_mieki256/ChrRectSet - GitHub
にアップしておきました。この記事に書いてあるソースは古いです。)
Unity上で Custom Font を使えばビットマップフォントが使えるようになるけれど。
1文字ずつUV座標等をGUIで入力していくのが面倒なので、なんとか自動化できないかと。何せ、1文字につき入力欄が10ヶ所、それを10〜95文字分とか、手入力してられないですわ…。
2種類の設定ができるようにしてみたり。
Unity上で Custom Font を使えばビットマップフォントが使えるようになるけれど。

2種類の設定ができるようにしてみたり。
- BMFontで出力したフォント画像 + .fntファイル(文字配置情報が書かれてる)を使って設定。
- 等間隔に配置されたフォント画像を使って設定。
◎ 使うフォント画像の例。 :
以下は、BMFontで出力したフォント画像の例。(M+フォントを使用。画像は256x256。) ギッチリ詰まってるからテクスチャが無駄にならないけど、計算で場所を求めていくのも、レタッチするのも、たぶん無理。
_配置情報(.fntファイル)
が無いと使えない。

以下は、等間隔で配置されたフォント画像の例。(Molotフォント使用。256x64。数字のみ10文字分。) これなら計算で配置が求められるし、レタッチ等も楽だけど、テクスチャは無駄だらけ。


以下は、等間隔で配置されたフォント画像の例。(Molotフォント使用。256x64。数字のみ10文字分。) これなら計算で配置が求められるし、レタッチ等も楽だけど、テクスチャは無駄だらけ。

◎ スクリプトの導入手順。 :
- Projects に Editor というフォルダを作る。
- Editorフォルダの中に C#ファイルを作成して、ChrRectSet.cs というファイル名にする。
- 下記ソースをコピペして保存。
- Unityのメニューに、Custom という項目が増えてるはず。
◎ 実行手順。 :
Custom → Custom Font Setting → Chr Rect Set を選択。ウインドウが開く。

BMFontの出力画像を使う場合。.fnt は .txt にリネームしておく。
等間隔に配置されたフォント画像を使う場合。
ただ、Setボタンを押して値が設定されても、すぐに変更が反映されるわけではないようで。Custom Font の Character Rects を開いたり閉じたりしてると、なんだかそのうち反映される模様。

BMFontの出力画像を使う場合。.fnt は .txt にリネームしておく。
- Assets から、「Custom Font」「フォント画像」「文字配置情報が書かれたテキストファイル(BMFontが出力した .fnt を .txt にリネームしたもの)」の3つをD&Dで登録。
- Setボタンを押せば、Custom Font の Character Rects に、テーブルファイルの内容が代入される。
等間隔に配置されたフォント画像を使う場合。
- ウインドウ表示後、Assets から、「Custom Font」「フォント画像」の2つをD&Dで登録して、「画像内でフォントが置かれている範囲」「横方向の文字数」「縦方向の文字数」「使う文字数」を入力してSetボタンを押せば設定される。
ただ、Setボタンを押して値が設定されても、すぐに変更が反映されるわけではないようで。Custom Font の Character Rects を開いたり閉じたりしてると、なんだかそのうち反映される模様。
◎ ソースコード。 :
_ChrRectSet.cs
どうせそのうち仕様が変わって使えなくなる予感。つーかフツーはNGUIとやらを買って使うのだろうからこんなの使わんですわな。
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
/*
* BMFontで出力した bitmap font 画像の文字配置情報を
* custom Font に設定するUnity拡張。
*
* Custom → Custom Font Setting → Chr Rect Set でウインドウを開いて、
* Custom Font、テーブルファイル(.txt)、フォント画像、
* の3つをD&Dで登録して Set ボタンを押せば、
* Custom Font の Character Rects を設定してくれる。
*
* 等間隔で配置された bitmpa font 画像に関しても設定できる機能付き。
* Custom Font、フォント画像、
* 画像内でフォント部分が置かれている範囲、
* 横方向の文字個数、縦方向の文字個数、使う文字個数、
* を入力して Set ボタンを押せば設定してくれる。
*/
public class ChrRectSet : EditorWindow {
public Font customFontObj;
public TextAsset fontPosTbl;
public Texture fontTexture;
public Rect useTexRect = new Rect(0, 0, 256, 256);
public int fontCountX = 8;
public int fontCountY = 8;
public int fontLength = 64;
struct ChrRect {
public int id;
public int x;
public int y;
public int w;
public int h;
public int xofs;
public int yofs;
public int index;
public float uvX;
public float uvY;
public float uvW;
public float uvH;
public float vertX;
public float vertY;
public float vertW;
public float vertH;
public float width;
}
// メニューに登録
[MenuItem("Custom/Custom Font Setting/Chr Rect Set")]
static void Init() {
EditorWindow.GetWindow(typeof(ChrRectSet));
}
// 表示ウインドウの内容
void OnGUI() {
// Custom Font 登録欄
customFontObj = (Font)EditorGUILayout.ObjectField("Custom Font", customFontObj, typeof(Font), false);
// フォント画像指定欄
fontTexture = (Texture)EditorGUILayout.ObjectField("Font Texture", fontTexture, typeof(Texture), false, GUILayout.Width(64), GUILayout.Height(64));
// 文字テーブルファイル登録欄
EditorGUILayout.Space();
EditorGUILayout.LabelField("Use Font Table Text File", EditorStyles.boldLabel);
fontPosTbl = (TextAsset)EditorGUILayout.ObjectField("Font Table Text File", fontPosTbl, typeof(TextAsset), false);
// 実行ボタン
if (GUILayout.Button("Set Character Rects")) {
if (customFontObj == null) this.ShowNotification(new GUIContent("No Custom Font selected"));
else if (fontTexture == null) this.ShowNotification(new GUIContent("No Font Texture selected"));
else if (fontPosTbl == null) this.ShowNotification(new GUIContent("No Font Position Table file selected"));
else CalcChrRect(customFontObj, fontPosTbl, fontTexture);
}
// 等分割して設定する場合の入力欄
EditorGUILayout.Space();
EditorGUILayout.LabelField("Grid", EditorStyles.boldLabel);
useTexRect = EditorGUILayout.RectField("Use Texture Area", useTexRect);
fontCountX = EditorGUILayout.IntField("Font Count X", fontCountX);
fontCountY = EditorGUILayout.IntField("Font Count Y", fontCountY);
fontLength = EditorGUILayout.IntField("Character Length", fontLength);
if (GUILayout.Button("Set Character Rects")) {
if (customFontObj == null) this.ShowNotification(new GUIContent("No Custom Font selected"));
else if (fontTexture == null) this.ShowNotification(new GUIContent("No Font Texture selected"));
else CalcChrRectGrid(customFontObj, fontTexture, useTexRect, fontCountX, fontCountY, fontLength);
}
}
// フォントテーブルを元にして設定
void CalcChrRect(Font fontObj, TextAsset posTbl, Texture tex) {
// フォント画像のサイズを取得
float imgw = (float)tex.width;
float imgh = (float)tex.height;
// 文字テーブルの内容を取得
string txt = posTbl.text;
List<ChrRect> tblList = new List<ChrRect>();
int asciiStartOffset = 128;
int maxH = 0;
foreach (string line in txt.Split('\n')) {
if (line.IndexOf("char id=") == 0) {
ChrRect d = GetChrRect(line, imgw, imgh);
if (asciiStartOffset > d.id) asciiStartOffset = d.id;
if (maxH < d.h) maxH = d.h;
tblList.Add(d);
}
}
ChrRect[] tbls = tblList.ToArray();
// index値を調整
for (int i = 0; i < tbls.Length; i++) {
tbls[i].index = tbls[i].id - asciiStartOffset;
}
// 新しい CharacterInfo を作成
SetCharacterInfo(tbls, fontObj);
}
// 等分割して設定
void CalcChrRectGrid(Font fontObj, Texture tex, Rect area, int xc, int yc, int num) {
float imgw = (float)tex.width;
float imgh = (float)tex.height;
int fw = (int)(area.width - area.x) / xc;
int fh = (int)(area.height - area.y) / yc;
List<ChrRect> tblList = new List<ChrRect>();
for (int i = 0; i < num; i++) {
int xi = i % xc;
int yi = i / xc;
ChrRect d = new ChrRect();
d.index = i;
d.uvX = (float)(area.x + (fw * xi)) / imgw;
d.uvY = (float)(imgh - (area.y + (fh * yi) + fh)) / imgh;
d.uvW = (float)fw / imgw;
d.uvH = (float)fh / imgh;
d.vertX = 0;
d.vertY = 0;
d.vertW = fw;
d.vertH = -fh;
d.width = fw;
tblList.Add(d);
}
ChrRect[] tbls = tblList.ToArray();
SetCharacterInfo(tbls, fontObj);
}
// 新しい CharacterInfo を Custom Font に上書き設定
void SetCharacterInfo(ChrRect[] tbls, Font fontObj) {
CharacterInfo[] nci = new CharacterInfo[tbls.Length];
for (int i = 0; i < tbls.Length; i++) {
nci[i].index = tbls[i].index;
nci[i].width = tbls[i].width;
nci[i].uv.x = tbls[i].uvX;
nci[i].uv.y = tbls[i].uvY;
nci[i].uv.width = tbls[i].uvW;
nci[i].uv.height = tbls[i].uvH;
nci[i].vert.x = tbls[i].vertX;
nci[i].vert.y = tbls[i].vertY;
nci[i].vert.width = tbls[i].vertW;
nci[i].vert.height = tbls[i].vertH;
}
fontObj.characterInfo = nci;
}
// フォントテーブルの1行分(1文字分)を構造体に記録
ChrRect GetChrRect(string line, float imgw, float imgh) {
ChrRect d = new ChrRect();
foreach (string s in line.Split(' ')) {
if (s.IndexOf("id=") >= 0) d.id = GetParamInt(s, "id=");
else if (s.IndexOf("x=") >= 0) d.x = GetParamInt(s, "x=");
else if (s.IndexOf("y=") >= 0) d.y = GetParamInt(s, "y=");
else if (s.IndexOf("width=") >= 0) d.w = GetParamInt(s, "width=");
else if (s.IndexOf("height=") >= 0) d.h = GetParamInt(s, "height=");
else if (s.IndexOf("xoffset=") >= 0) d.xofs = GetParamInt(s, "xoffset=");
else if (s.IndexOf("yoffset=") >= 0) d.yofs = GetParamInt(s, "yoffset=");
}
// Uv情報を算出
d.uvX = (float)d.x / imgw;
d.uvY = (float)(imgh - d.y - d.h) / imgh;
d.uvW = (float)d.w / imgw;
d.uvH = (float)d.h / imgh;
// Vert情報を算出
//d.vertX = (float)d.xofs;
d.vertX = 0.0f;
d.vertY = -(float)d.yofs;
d.vertW = d.w;
d.vertH = -d.h;
// widthを算出
d.width = d.w;
return d;
}
// "wd=数値を示す文字列" を数値にして返す
int GetParamInt(string s, string wd) {
if (s.IndexOf(wd) >= 0) {
int v;
if (int.TryParse(s.Substring(wd.Length), out v)) return v;
}
return int.MaxValue;
}
}
ソースのライセンスは…Unityってそのあたり何か強制されるのかな? 特にそういう制限がないのであれば Public Domain ってことで。どうせそのうち仕様が変わって使えなくなる予感。つーかフツーはNGUIとやらを買って使うのだろうからこんなの使わんですわな。
[ ツッコむ ]
以上です。

