Unity中UGUI使用fnt位图字体

Cocos中,支持fnt格式的字体文件,但是Unity只支持ttf,和otf的字体文件,所以想用位图字体,就得自己造咯!!!

下图是我们想要的文字样式:
美工能给出的图片文件样式:

这里啰嗦一句,数字和点的图片高度最好一致,这样Text文字不会参差不齐。

一、使用BMFont工具,生成位图字体文件()

  工具下载地址:http://www.angelcode.com/products/bmfont/

  1. 点击 Options==>Export Options,修改导出配置

   主要是Bit depth得改为32,Padding可改,可不改,Height本来为256,我修改为32,可以减去生成png中很大一片空白。
    再说一下,Font descriptor,这里我选择的是Text。一般选择Text 或 XML,主要会影响到生成的fnt文件内容的格式,以及后面计算每个字符参数用到的解析方式。

  2. 点击 Edit ===> Open Image manager,打开图片管理窗口
  3. 点击 Image==>Import image... 导入图片,修改Id为与字符对应的Ascii码,例如0的Ascii码为48;
  4. 图片导入完毕后,点击 Options==>Save bitmap font as... 导出字体文件(fnt&&png)

二、制作Unity中的Custom Font

  1. 将fnt,png格式文件,导入Unity项目中

    上面是默认的配置,之前我这边显示出错,网上有文章说png文件配置需要修改,比如Alpha Source改成Form Gray Scale、Sprite Mode改成 Mutiple等等。我尝试改了,也不顶用,实际上,成功的时候,啥也不用改,主要问题还是在计算字体文件item的UV(x,y,w,h)。

  2. 再Editor文件夹下,新建BMFontEditor.cs文件
using UnityEngine;
using UnityEditor;
using System.Collections.Generic;

//用于生成 custom font 和 Meterial 文件
//并且配置字体中item的参数
public class BMFontEditor : EditorWindow
{
    public TextAsset fontPosTbl;
    public Texture fontTexture;
    public Vector2 scrollPos;

    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;
    }


    // add menu
    [MenuItem("Tools/BMFontTools")]
    static void Init()
    {
        EditorWindow.GetWindow(typeof(BMFontEditor));

    }

    // layout window
    void OnGUI()
    {
        //EditorGUILayout.BeginScrollView(scrollPos);
        EditorGUILayout.BeginVertical();

        // use .fnt(.txt)
        fontTexture = (Texture)EditorGUILayout.ObjectField("Font Texture", fontTexture, typeof(Texture), false);

        EditorGUILayout.Space();
        EditorGUILayout.LabelField("Use BMFont fnt File", EditorStyles.boldLabel);
        EditorGUILayout.LabelField("(must Font Descriptor select Text)", EditorStyles.boldLabel);

        fontPosTbl = (TextAsset)EditorGUILayout.ObjectField("BMFont fnt (.fnt)", fontPosTbl, typeof(TextAsset), false);
        if (GUILayout.Button("Create(创建后必须重启生效)"))
        {

            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(fontPosTbl, fontTexture);
            }
        }
        EditorGUILayout.LabelField("(Must Restart unity  valid!)", EditorStyles.whiteBoldLabel);
        EditorGUILayout.LabelField("*********************************", EditorStyles.largeLabel);
        EditorGUILayout.EndVertical();
        //EditorGUILayout.EndScrollView();
    }

    void OnInspectorUpdate()
    {
        this.Repaint();
    }


    void CalcChrRect(TextAsset posTbl, Texture tex)
    {
        string fileName = AssetDatabase.GetAssetPath(fontPosTbl);
        string texName = AssetDatabase.GetAssetPath(tex);
        string fontName = System.IO.Path.GetFileNameWithoutExtension(fileName);
        string fontPath = fileName.Replace(".fnt", ".fontsettings");
        string matPath = fileName.Replace(".fnt", ".mat");
        float imgw = (float)tex.width;
        float imgh = (float)tex.height;
        string txt = posTbl.text;

        List<ChrRect> tblList = new List<ChrRect>();
        foreach (string line in txt.Split('\n'))
        {
            if (line.IndexOf("char id=") == 0)
            {
                ChrRect d = GetChrRect(line, imgw, imgh);
                tblList.Add(d);
            }
        }
        if (tblList.Count == 0)
        {
            new GUIContent("Failed");
            return;
        }

        ChrRect[] tbls = tblList.ToArray();
        Font font = new Font();
        font.name = fontName;
        SetCharacterInfo(tbls, font);


        Material mat = new Material(Shader.Find("UI/"));
        mat.mainTexture = tex;
        mat.name = fontName;
        font.material = mat;


        Debug.Log(System.IO.Path.GetFileNameWithoutExtension(fileName));
        Debug.Log(fileName);

        AssetDatabase.CreateAsset(mat, matPath);
        AssetDatabase.CreateAsset(font, fontPath);
        AssetDatabase.SaveAssets();
        this.ShowNotification(new GUIContent("Complete"));
        AssetDatabase.Refresh(ImportAssetOptions.ForceUpdate);
    }

    // over write custom font by new CharacterInfo
    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].advance = (int)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;
    }

    // get font table one line. 重点在这
    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=");
            else if (s.IndexOf("xadvance=") >= 0) d.width = GetParamInt(s, "xadvance=");
        }
        d.index = d.id;
        d.uvX = (float)d.x / imgw;
        d.uvY = (float)(imgh - (d.y)) / imgh;     
        d.uvW = (float)d.w / imgw;
        d.uvH = (float)-d.h / imgh;  
        d.uvH = (float)d.h / imgh;

        d.vertX = (float)d.xofs;
        d.vertY = (float)-d.yofs;     
        d.vertW = d.w;
        d.vertH = d.h;

        return d;
    }

    // "wd=int" to int
    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;
    }
}

    然后,菜单栏会多出来一个Tools选项
  3. 点击Tools==>BMFontTools,拖拽fnt和png文件
  4. 点击Create,创建字体文件

    以下是生成的Custom font 和 Meterial文件:
  5. 在工程中新建Text组件,将生成的字体文件,材质,拖到Text属性中

   

  6. 调整Color,设置颜色值为图片中最浅的颜色

  例如:根据我的图片,我就设置为金黄色部分颜色值,如果图片中白色,一般去白色部分的颜色值。然后结果就出来了:

三、讲解细节

  用txt文本打开fnt文件,
fnt文件内容
CustomFont属性

看一下对应关系(这里是对应于png图片是原始图片,即没有做垂直翻转):
  Index:char Id
  Uv:
    X: x/scaleW                  Y: 1 - y/scaleH
    W: width/scaleW               H: -height/scaleH
  Vert :
    X: xoffset                    Y: -yoffset
    W: width                     H: height
  Width: xadvance

最后要注意的几个点就是:

  1. 如果还没有显示的话,可能是Text组件的高度不够
  2. 如果换行发现字体重叠了,就修改字体文件属性Line Spacing的值
  3. 要改变字体大小,修改字号不会有用了,要修改Scale的x,y,最好修改为一样的比例,不然会被拉伸哦
  4. 有一种情况下,文字会是默认的黑色,需要调整文字颜色到相近的色号,才能达到渐变的效果,后来的测试中,拖了材质,就能显示出效果了,没有复现出之前的问题,只能记录一下了。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269