位图字体显示成错误的字符,怎么修
你加载了字体,画 SCORE,出来的是 TDPSF。 文件是合法的,引擎没问题,每个字形也都画得好好的——只是不是你要的那个。 这篇讲清为什么,以及生成的位图字体另外四种出错方式。
1. 先确认这是「错位」
把错的输出和对的输入逐位对照。如果你拿到的每一个字符, 都在字符表上比你想要的那个偏了同样的距离——S 给了你 T,C 给了你 D,全都往右一位—— 那么映射是整体错位。
这一步值得先做,因为整体错位只有一类原因、一种修法; 而「只有一部分字符错」是另一个问题——通常是某个字形被拆成了两个, 它会让它之后的全部移位,但它之前的都是对的。
所以更锐利的测试是:把你的整张字符表按顺序渲染成一整串。 第一个错字形之前的全都是对的。要看的就是那个位置。
2. 让它错位的五种原因
生成字体时框和字符是按顺序配对的:第一个框拿第一个字符。 任何改变了「某个位置之前有多少个框」的东西,都会让它之后的全部移位。
一个杂散像素。页边留下的一个不透明像素—— 橡皮没擦干净的那种——和别的连通区域没有区别,会被算成一个字形。 这是最常见的原因,遥遥领先;也是最看不见的: 在深色背景上按 1:1 看,一个像素根本不会引起注意。
一个空白单元格。如果图是按网格排的, 而某个格子是空的——还没画的字符,或者留给空格的那一格—— 自动识别在那里什么都找不到,因为本来就没有东西可找。 可你的字符表里还有一个字符对应它。它之后的全都往反方向移位。
由两片组成的字形。i 的点、: 的两个点、! 的那一横、= 的两条横。 分开计数的话,每一个都多出一个框。 而按距离合并的工具有相反的毛病——它会同样乐意地把相邻两个字母合并—— 所以规则必须是按水平包含关系合并: 点在它自己竖画的正上方,而不在旁边那个字母的正上方。
字符表里的空白字符。末尾多打的一个空格,或者从别处粘贴时带进来的换行, 和任何字符一样会占掉一个框。而它在输入框里是看不见的。
不按阅读顺序排的图。识别会把框从左到右、从上到下排序,按垂直重叠分行。 如果图的各行是错开的,或者某一行的高字形侵入了下一行的带, 排出来的顺序就可能不是你画的顺序。少见,但它看起来和错位一模一样。
3. 定位偏移从哪里开始
把字符表渲染成文字,从头读到第一个错的字符。 假设你的字符表是 ABCDEFGHIJ…,输出读到 G 都是对的,然后开始错。那么不一致出现在第八个框, 多出来或少掉的那个就在图上 H 的位置或者它前面一点。
然后带着画在图上的识别框去看那个位置。 三种情况必有其一:一个框里什么都没有、一个字符被两个框圈着、 或者一个字符没有框。每一种都对应上面的一条原因。
字符表很长时这也比听上去快—— 你找的是第一处不一致,不是逐个比对整串, 而第一处不一致通常就在第一行。
4. 另外四种故障
整体发糊。这不是字体的问题。 是贴图过滤在像素之间做插值:设成邻近采样——Unity 是 Point, Godot 和 LibGDX 是 Nearest,Phaser 是 pixelArt: true。 如果只在某些位置发糊,那是字体被画在了非整数坐标上, 绘制前把位置取整。
字母挤在一起或者离太远。那是步进宽度——每个字形画完之后笔往前移多少。 从紧贴裁剪的框生成的字体需要加一两像素字间距; 从没有裁剪的网格单元格生成的则相反,每个字母都占满一格。 调字间距时对着真实文字调,别对着字母表—— 字母表里没有短单词。
空格没有了。空格没有像素,任何算法都识别不到它,它永远不会是那些框之一, 所以它的宽度必须显式地设。 另外,如果你的字符表里含有一个空格,那个空格还会占掉一个本不该占的框—— 也就是上面的第四条原因;这也是这两个症状经常一起出现的原因。
字母在行上跳来跳去。每个字形的垂直偏移是从它自己那一行的顶部量起的, 所以高矮不同的字形会待在它们被画的位置。 如果某一行最高的那个字形缺席——这一行里没有 A 也没有 l——那这一行的顶部就是按一个更矮的东西量的,整行都会偏高。 画图时让每一行至少有一个满高的字形,或者手动设偏移。
5. 怪字体之前先看这两条
有两件引擎侧的事看起来像字体的 bug,其实不是。
.fnt 是按文件名引用它那张图的。 如果两者不在同一个目录,或者字体生成之后图片被改过名, 加载器要么直接失败,要么——在少数引擎里——一声不响地什么都不画。
还有,.fnt 不是 .ttf。 引擎给位图字体准备的是另一套加载器: Phaser 里是 load.bitmapFont 而不是 CSS 字体, LibGDX 里是 BitmapFont 而不是 FreeTypeFontGenerator。 把 .fnt 喂给错的那个,报出来的错和你的字符毫无关系。
常见故障
每个字符都比正确的往后一位
第一个错字符之前多了一个框——杂散像素,或者某个字形被算成了两片。
每个字符都比正确的往前一位
少了一个框:识别不到的空白单元格,或者字符表里有个字符根本没画。
前半段是对的,后半段是错的
错位是从中间某处开始的。找到第一个错的字符,去图上那个位置看。
只有 i、j、! 和 : 是错的
这几个正是由两片组成的字形。它们被数了两次。
单词之间连在一起没有空隙
空格没有设宽度。它识别不到——因为它没有像素。
字体在一个引擎里能用,在另一个里不行
检查 .fnt 是不是和它引用的那张图放在一起, 以及你用的是不是位图字体的加载器而不是 TrueType 的。