今はCocos2d-xを使ってゲームを作っていて、ゲーム中のラベルやスコアの表示とかにビットマップフォントを使っているのだけど、使っているフォント(気にいったデザインのフォント)が固定幅のフォントではないので、スコアが変わるたびに幅がガタガタ変わってしまう。スコアの表示だけ固定幅のものを使えばいいのだけど、余計なテクスチャは持たせたくないところ。そこでフォントのデータ(拡張子がfntのファイル)を修正して数字のところだけ固定幅にしてしまおう、というお話です。
話の前提として、fntファイルの作成にはBMFontを使っていて、Export OptionsでTextファイルを出力するようにしています。
出力されたfntファイルの数字の文字のところを抜きだすと、こんな感じになっている(数字の「0」はAsciiコードで48)。
1 2 3 4 5 6 7 8 9 10 |
char id=48 x=541 y=165 width=72 height=78 xoffset=-3 yoffset=60 xadvance=66 page=0 chnl=15 char id=49 x=59 y=347 width=50 height=76 xoffset=-3 yoffset=61 xadvance=43 page=0 chnl=15 char id=50 x=850 y=241 width=56 height=77 xoffset=-4 yoffset=60 xadvance=49 page=0 chnl=15 char id=51 x=244 y=255 width=57 height=78 xoffset=-4 yoffset=60 xadvance=49 page=0 chnl=15 char id=52 x=907 y=240 width=68 height=76 xoffset=-5 yoffset=61 xadvance=60 page=0 chnl=15 char id=53 x=792 y=243 width=57 height=77 xoffset=-3 yoffset=61 xadvance=51 page=0 chnl=15 char id=54 x=893 y=161 width=66 height=78 xoffset=-3 yoffset=60 xadvance=59 page=0 chnl=15 char id=55 x=359 y=255 width=53 height=78 xoffset=-4 yoffset=61 xadvance=44 page=0 chnl=15 char id=56 x=825 y=162 width=67 height=78 xoffset=-4 yoffset=60 xadvance=59 page=0 chnl=15 char id=57 x=0 y=268 width=65 height=78 xoffset=-4 yoffset=60 xadvance=58 page=0 chnl=15 |
xとyはビットマップ上のどこに文字があるかを示していて、widthとheightが文字の大きさ。でも文字幅を決めているのはxadvanceで、文字を書いたあとこの値の分だけ横のずらしたところに次の文字を書くことになっている。
なのでxadvanceを揃えてやれば固定幅になるというわけ。でもこの値だけ変えてしまうと文字が全体的に左よりになってしまうのでxadvanceを増やしたら、増やした値の半分だけxoffsetも増やしてやる方がいい。xoffsetは文字の位置にビットマップを置く際のオフセットを表している。
それで実際にxadvanceを揃えたのがこちら。一番大きな幅66に揃えてみた。
1 2 3 4 5 6 7 8 9 10 |
char id=48 x=541 y=165 width=72 height=78 xoffset=-3 yoffset=60 xadvance=66 page=0 chnl=15 char id=49 x=59 y=347 width=50 height=76 xoffset=8 yoffset=61 xadvance=66 page=0 chnl=15 char id=50 x=850 y=241 width=56 height=77 xoffset=5 yoffset=60 xadvance=66 page=0 chnl=15 char id=51 x=244 y=255 width=57 height=78 xoffset=5 yoffset=60 xadvance=66 page=0 chnl=15 char id=52 x=907 y=240 width=68 height=76 xoffset=-2 yoffset=61 xadvance=66 page=0 chnl=15 char id=53 x=792 y=243 width=57 height=77 xoffset=5 yoffset=61 xadvance=66 page=0 chnl=15 char id=54 x=893 y=161 width=66 height=78 xoffset=-1 yoffset=60 xadvance=66 page=0 chnl=15 char id=55 x=359 y=255 width=53 height=78 xoffset=8 yoffset=61 xadvance=66 page=0 chnl=15 char id=56 x=825 y=162 width=67 height=78 xoffset=0 yoffset=60 xadvance=66 page=0 chnl=15 char id=57 x=0 y=268 width=65 height=78 xoffset=1 yoffset=60 xadvance=66 page=0 chnl=15 |
これで数字の部分だけ固定幅になってめでたしめでたし、と思ったのだけど、実際にはそううまくはいかなかった。
スコアとかを表示するときにAnchorPointをセットして右側に揃えるようにしていたのだが、最後の文字(数字)によってラベルの幅が変わってしまって、依然として少しガタガタしてしまう。しょうがないのでCocos2d-xのソースコードを覗いてみることに。問題の部分はこちら(v3.4あたりのソースで、ラベルの表示にはLabelクラスを使ってます)。CCLabelTextFormatter.cppの402行目あたり。createStringSpritesという関数の中です。人様のコードだけど引用だけならいいよね。
1 2 3 4 5 6 7 8 9 10 11 |
// If the last character processed has an xAdvance which is less that the width of the characters image, then we need // to adjust the width of the string to take this into account, or the character will overlap the end of the bounding // box if(charAdvance < lastCharWidth) { tmpSize.width = longestLine - charAdvance + lastCharWidth; } else { tmpSize.width = longestLine; } |
丁寧にコメントがついてました。xAdvanceが実際の文字の画像の幅より小さいときに文字が境界からはみだしちゃうので、あえて幅を調整しているようです。まあ確かに右揃えで右端に余白がない場合ははみだしちゃ困るわけだけど、それはそういうフォントを使う方が悪いと思う。実際今回のケースもはみだしちゃっていたわけだけど、そこは自己責任ということで、がっつり if (charAdvance < lastCharWidth)
のブロックを削除(elseブロックは残す)。
これできれいに固定幅の数字になりました。めでたし、めでたし。