Toolkitly

位图字体工具

把画好的字符图切成一个个字形,指定哪个框是哪个字符,然后直接打字看它排出来的样子。 可导出 BMFont 的 .fnt 或 JSON。免费、无需注册, 所有像素都留在你的浏览器里。

不会上传任何文件——切割和排版都在你的浏览器里完成。

怎么把一张字符图变成字体

四步,出问题的是第三步。

  1. 1载入字符图把 PNG、WebP、GIF 或 JPEG 拖到虚线框里,或点击选择文件,也可以直接从剪贴板粘贴。 背景透明,字符按阅读顺序排列。
  2. 2让它找出字符只要字形之间有间隙,自动识别在任何排布下都管用。 如果你的图是规整的单元格表格,切到网格模式,填上列数和行数。
  3. 3把字符表对齐第一个框对应第一个字符。这一步是最容易错的,而预览就是用来抓这个错的—— 如果读出来是乱码,说明字符表和图错开了。
  4. 4调好间距然后导出字间距、行间距、空格宽度。然后导出 .fnt, 和它引用的那张图放在一起。

为什么字符图不是精灵图

找字符这件事看起来和找动画帧一样,大部分也确实一样: 把连成一片的实心像素泛洪出来,按阅读顺序排好。本站的精灵图切割工具做的正是这件事,这个工具直接复用了同一套识别代码。

然后就分岔了,因为一个字形常常不止一片i 的点不挨着它的竖画。: 的两个点谁也不挨着。= 是两条分开的横。不做处理的话, 识别会把每一片都当成一个独立字符,它后面整串就全部移位了。

精灵图解决同类问题的办法是把靠得近的片合并—— 角色脱手飞出去的剑属于这个角色。这条规则用在字符图上是错的: 相邻的字母也靠得很近,按距离合并会把 rn 合成一个根本不是字母的东西。

所以这里改成按水平包含关系合并。 点正好在竖画的正上方,所以一个框的列范围落在另一个框的列范围之内; 而并排的两个字母不会,哪怕它们互相压过去一两个像素。 这一处改动,就是精灵图切割器和字体切分器之间的主要差别。

真正会出错的地方

生成位图字体时几乎所有的问题都是同一个问题: 字符表和图错开了,而且是错开一位。

页边一个杂散像素会被算成一个字形。以空白单元格开头的图不会—— 因为空白没有东西可识别。一个 i 的点如果画得偏左一个像素, 就通不过包含判断,于是变成两个字符。以上任何一种都会让它后面的每个字符移位, 而生成的文件完全合法——它只是给出错之后的每个字母都配错了字形。

所以预览不是装饰。打一个词读一读。 如果出来是无意义的东西,偏移就发生在第一个错字母之前, 而画在图上的那些框会告诉你在哪。 工具另外还会报出「排在字符表之外的框」和「没有框可用的字符」, 这两条通常直接把问题说穿了。

什么时候用得上

你自己画的字体

在 Aseprite 里画好的像素字体,本质上就是一张字符图,别的什么都没有。 这个工具就是从那张图到「引擎能拿来排字的东西」之间的那一步。

只给了图没给间距的字体

素材包经常只发一张 PNG,间距要你自己试出来。 导进来,对着真实文字把字间距调顺眼,导出 .fnt。

改一套现成字体的间距

载入字符图,调字间距和行间距, 直到示例文字在你实际使用的尺寸下读着舒服,再重新导出。

字体上线前先验一遍

把游戏里真正会显示的字符串打进去——最长的那条菜单、带小数点的数字—— 看看是不是每个字符都有。

导出格式

.fnt——AngelCode 的 BMFont 文本格式。 每个字形一行 char,带矩形、偏移和步进, 外加每个解析器都要读的 infocommonpage 三行。它按文件名引用你的图片,所以两者要放在同一个目录里。

.json——同样的数据,换成在文本编辑器里更好读、 也更好喂给自己写的加载器的形状。

预览图 PNG——你打的那段字,用这套字体排好,按原始尺寸导出。 做标题、截图,或者拿到浏览器外面检查间距时有用。

在引擎里怎么加载

下面每一种都要 .fnt 和 PNG 放在一起。

Phaser 3

this.load.bitmapFont(key, 'font.png', 'font.fnt'), 然后 this.add.bitmapText(x, y, key, text)。 游戏配置里加上 pixelArt: true

LibGDX

new BitmapFont(Gdx.files.internal("font.fnt"))。 贴图的 min 和 mag 过滤都要设成 Nearest, 否则相机一不在 1:1 字就糊了。

love2d

love.graphics.newFont("font.fnt") 直接读 BMFont。 记得对字体的图片调用 setFilter("nearest", "nearest")

常见问题

.fnt 文件是什么?

AngelCode 的 BMFont 格式:一个文本文件,逐个字符地记录它占图片上的哪个矩形、画完之后笔要往前移多少。图片和 .fnt 是一起走的——.fnt 里按文件名引用图片,所以两个文件要放在一起。Phaser、LibGDX、love2d 以及大多数位图字体加载器都能读。

它怎么知道一个字符到哪里结束、下一个从哪里开始?

自动模式用泛洪找出连成一片的实心像素,和精灵图切割工具找帧是同一套。字体特有的是接下来那一步:一个字形常常不止一片——i 的点、冒号的两个点、感叹号的那一横——所以当一片在水平方向被另一片包住时,两片会被合并。如果像精灵图那样按距离合并,那它会同样乐意地把相邻的两个字母合成一个。

预览出来是乱码,哪里出了问题?

字符串和图错开了,而且几乎总是错开一位。第一个框对应第一个字符、第二个对应第二个,只要图开头有个空格、或者某处有个杂散像素,后面全都会移位。工具会分别报出「有框没有字符」和「有字符没有框」,通常足以定位偏移是从哪里开始的。

支持不等宽字体吗?

默认就是。每个字形按自己测出来的宽度加字间距往前走,所以 I 占的位置比 W 少。网格模式下每个单元格会先裁到实际有像素的范围,所以即使是规整网格也能生成不等宽字体——想要等宽的话调字间距就行。

空格怎么办?

空格没有像素,任何算法都检测不到它。它的宽度由你来设,默认是最宽字形的一半,对多数像素字体来说八九不离十。

能用在非拉丁字体上吗?

可以。字符表就是一串文本,把任意字符按它们在图上出现的顺序填进去即可,每个字形都会带着自己的码点写进文件。至于你的引擎能不能渲染它们,那取决于引擎,不取决于这个文件。

图片会被上传到服务器吗?

不会。字符图是用浏览器的 Canvas API 在本地解码、切割和排版的。没有任何数据离开你的电脑,页面加载完之后断网也照样能用。

相关教程

相关工具