Unity的UGUI的文字渲染效率应该是挺高的,一般来说用默认的Text控件,TTF的Font就满足需求了。不过有时候需要渲染艺术字体的时候还是需要用到BMFont。
一、BMFont的基础使用,创建fnt字体的步骤就不多说了。这里额外提一下就是BMFont是支持命令行的。这里贴一下我使用的脚本,更新字体比较方便。
bmfc文件是BMFont的配置文件,使用BMFont的GUI程序设置并保存好对应的字体配置(配置里面包含纹理大小、特殊字符对图片的映射关系等等)
txt文件是一个纯文本,包含所需要生成字体的所有文字
# -*- coding: utf-8 -*-
import os,sys,shutil,subprocess,glob
global SOURCE_PATH
global TARGET_PATH
SOURCE_PATH = 'font/'
TARGET_PATH = '../Assets/Font'
COMMON_TEXT = "text.txt"
CMD = 'support/BMFont/bmfont.exe'
def genFont(configPath, txtPath):
fntPath = configPath.replace('.bmfc', '.fnt');
pngPathOld = configPath.replace('.bmfc', '_0.png');
pngPathNew = configPath.replace('.bmfc', '.png');
subprocess.call('"{0}" -c {1} -o {2} -t {3}'.format(CMD, configPath, fntPath, txtPath))
if os.path.exists(pngPathNew):
os.remove(pngPathNew);
os.rename(pngPathOld, pngPathNew);
fileData = []
fp = open(fntPath, 'r');
for line in fp:
if line.find('file="') != -1:
fileData.append(line.replace('_0.png', '.png'));
else:
fileData.append(line);
fp.close();
fpw = open(fntPath, 'w');
fpw.writelines(fileData);
fileList = glob.glob(SOURCE_PATH + '*.bmfc')
for file in fileList:
txtFile = file.replace(".bmfc", ".txt")
if os.path.exists(txtFile):
genFont(file, txtFile)
else:
genFont(file, COMMON_TEXT)
os.system('PAUSE')
二、Unity中的特殊字体是使用CustomFont。 CustomFont是一个资源,跟ttf字体等价,里面包含了字体的字形信息等数据。之前NGUI有一套脚本可以通过fnt文件创建CustomFont。 我写了一个脚本用来做类似的事情。
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
// 创建bmfont
public class CreateFontEditor : Editor
{
[MenuItem("Assets/CreateBMFont")]
static void CreateFont()
{
Object obj = Selection.activeObject;
string fntPath = AssetDatabase.GetAssetPath(obj);
if (fntPath.IndexOf(".fnt") == -1) {
// 不是字体文件
return;
}
string customFontPath = fntPath.Replace(".fnt", ".fontsettings");
if (!File.Exists(customFontPath)) {
return;
}
Debug.Log(fntPath);
StreamReader reader = new StreamReader(new FileStream(fntPath, FileMode.Open));
List<CharacterInfo> charList = new List<CharacterInfo>();
Regex reg = new Regex(@"char id=(?<id>\d+)\s+x=(?<x>\d+)\s+y=(?<y>\d+)\s+width=(?<width>\d+)\s+height=(?<height>\d+)\s+xoffset=(?<xoffset>\d+)\s+yoffset=(?<yoffset>\d+)\s+xadvance=(?<xadvance>\d+)\s+");
string line = reader.ReadLine();
int lineHeight = 0;
int texWidth = 1;
int texHeight = 1;
while (line != null) {
if (line.IndexOf("char id=") != -1) {
Match match = reg.Match(line);
if (match != Match.Empty) {
var id = System.Convert.ToInt32(match.Groups["id"].Value);
var x = System.Convert.ToInt32(match.Groups["x"].Value);
var y = System.Convert.ToInt32(match.Groups["y"].Value);
var width = System.Convert.ToInt32(match.Groups["width"].Value);
var height = System.Convert.ToInt32(match.Groups["height"].Value);
var xoffset = System.Convert.ToInt32(match.Groups["xoffset"].Value);
var yoffset = System.Convert.ToInt32(match.Groups["yoffset"].Value);
var xadvance = System.Convert.ToInt32(match.Groups["xadvance"].Value);
CharacterInfo info = new CharacterInfo();
info.index = id;
float uvx = 1f*x/texWidth;
float uvy = 1 - (1f*y/texHeight);
float uvw = 1f*width/texWidth;
float uvh = -1f*height/texHeight;
info.uvBottomLeft = new Vector2(uvx, uvy);
info.uvBottomRight = new Vector2(uvx + uvw, uvy);
info.uvTopLeft = new Vector2(uvx, uvy + uvh);
info.uvTopRight = new Vector2(uvx + uvw, uvy + uvh);
info.minX = xoffset;
info.minY = yoffset + height / 2; // 这样调出来的效果是ok的,原理未知
info.glyphWidth = width;
info.glyphHeight = -height; // 同上,不知道为什么要用负的,可能跟unity纹理uv有关
info.advance = xadvance;
charList.Add(info);
}
} else if (line.IndexOf("scaleW=") != -1) {
Regex reg2 = new Regex(@"common lineHeight=(?<lineHeight>\d+)\s+.*scaleW=(?<scaleW>\d+)\s+scaleH=(?<scaleH>\d+)");
Match match = reg2.Match(line);
if (match != Match.Empty) {
lineHeight = System.Convert.ToInt32(match.Groups["lineHeight"].Value);
texWidth = System.Convert.ToInt32(match.Groups["scaleW"].Value);
texHeight = System.Convert.ToInt32(match.Groups["scaleH"].Value);
}
}
line = reader.ReadLine();
}
Font customFont = AssetDatabase.LoadAssetAtPath<Font>(customFontPath);
customFont.characterInfo = charList.ToArray();
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
Debug.Log(customFont);
}
}
这个脚本干的事情就是读取并解析fnt文件,获取每个字形的坐标,创建CharactorInfo对象并给它附上计算后的uv坐标,最终结果保存早Font里面。需要留意的是unity的uv坐标是左下角为原点。