Unity自定义美术字体(图片字体fontsettings)

本文内容

开发过程有个很常见的需求:使用美术提供的图片来表现美术字体。

Unity官方为此提供了一个自定义字体,用于在UI上显示图片字体,具体各参数意义可以参考https://docs.unity3d.com/2022.2/Documentation/Manual/class-Font.html
请添加图片描述

效果

如下,我们可以很自由的展示图片字体。
请添加图片描述
请添加图片描述

工具代码

using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;

public class CrisFontWindow: EditorWindow
{
    private string fontPath = "Assets/crisFont.fontsettings";
    private Font font;
    private Rect[] rects;
    private TextAsset characterCfg;

    void OnGUI()
    {
        GUILayout.BeginVertical();

        GUILayout.BeginHorizontal();
        GUILayout.Label("字体:", GUILayout.Width(100.0f));
        font = (Font)EditorGUILayout.ObjectField(font, typeof(Font), true);
        GUILayout.EndHorizontal();

        if (font == null)
        {
            EditorGUILayout.BeginHorizontal();
            GUILayout.Label("字体路径", GUILayout.Width(100));
            fontPath = GUILayout.TextField(fontPath);
            EditorGUILayout.EndHorizontal();

            if (GUILayout.Button("新建字体"))
            {
                Font newFont = new Font();
                try
                {
                    AssetDatabase.CreateAsset(newFont, fontPath);
                    font = AssetDatabase.LoadAssetAtPath<Font>(fontPath);
                }
                catch
                {

                }
                if (font == null) Debug.LogError("路径错误:" + fontPath);
            }
        }
        else
        {
            GUILayout.BeginHorizontal();
            GUILayout.Label("字体配置:", GUILayout.Width(100.0f));
            characterCfg = (TextAsset)EditorGUILayout.ObjectField(characterCfg, typeof(TextAsset), true);
            GUILayout.EndHorizontal();
            GUILayout.Label("请选中需要打入的字体文件");
            GUILayout.BeginHorizontal();
            if (GUILayout.Button("更新文本")) UpdateFont();

            GUILayout.EndHorizontal();
        }

        GUILayout.EndVertical();
    }

    void UpdateFont()
    {
        string fontPath = AssetDatabase.GetAssetPath(font);
        string fontDir = Path.GetDirectoryName(fontPath);
        var texSavePath = Path.Combine(fontDir, Path.GetFileNameWithoutExtension(fontPath) + ".png");
        CombineTexture(texSavePath);
        var tex = AssetDatabase.LoadAssetAtPath<Texture2D>(texSavePath);
        if (tex == null) Debug.LogError("未发现生成的图集");

        var matPath = Path.Combine(fontDir, Path.GetFileNameWithoutExtension(fontPath) + ".mat");
        var mat = AssetDatabase.LoadAssetAtPath<Material>(matPath);
        if (mat == null)
        {
            var newMat = new Material(Shader.Find("GUI/Text Shader"));
            AssetDatabase.CreateAsset(newMat, matPath);
            mat = newMat;
        }
        mat.SetTexture("_MainTex", tex);
        EditorUtility.SetDirty(mat);
        var list = new List<CharacterInfo>();

        float maxHeight = 0;
        for (int i = 0; i < rects.Length; i++)
        {
            if (rects[i].height > maxHeight) maxHeight = rects[i].height;
        }

        for (int i = 0; i < rects.Length; i++)
        {
            Rect rect = ConvertToPixels(rects[i], tex.width, tex.height);
            int asciiIndex = ac[i];
            var charInfo = new CharacterInfo();
            charInfo.index = asciiIndex;

            Rect uv = ConvertToTexCoords(rect, tex.width, tex.height);
            charInfo.uvBottomLeft = new Vector2(uv.x, uv.y);
            charInfo.uvTopLeft = new Vector2(uv.x, uv.y + uv.height);
            charInfo.uvBottomRight = new Vector2(uv.x + uv.width, uv.y);
            charInfo.uvTopRight = new Vector2(uv.x + uv.width, uv.y + uv.height);

            charInfo.minX = 0;
            charInfo.maxX = (int)rect.width;
            charInfo.minY = (int)(tex.height / 2 - (maxHeight - rect.height) / 2 - rect.height);
            charInfo.maxY = (int)(tex.height / 2 - (maxHeight - rect.height) / 2);

            charInfo.advance = (int)rect.width;

            list.Add(charInfo);
        }
        font.material = mat;
        font.characterInfo = list.ToArray();
        EditorUtility.SetDirty(font);
        AssetDatabase.SaveAssets();
        AssetDatabase.Refresh();
        Debug.Log("更新成功");
    }

    Rect ConvertToPixels(Rect rect, int width, int height)
    {
        Rect r = rect;
        r.xMin = Mathf.RoundToInt(rect.xMin * width);
        r.xMax = Mathf.RoundToInt(rect.xMax * width);
        r.yMin = Mathf.RoundToInt((1 - rect.yMax) * height);
        r.yMax = Mathf.RoundToInt((1 - rect.yMin) * height);
        return r;
    }

    Rect ConvertToTexCoords(Rect rect, int width, int height)
    {
        Rect r = rect;
        if (width != 0 && height != 0)
        {
            r.xMin = rect.xMin / width;
            r.xMax = rect.xMax / width;
            r.yMin = 1 - rect.yMax / height;
            r.yMax = 1 - rect.yMin / height;
        }
        return r;
    }

    List<int> ac = new List<int>();
    void CombineTexture(string texSavePath)
    {
        ac.Clear();
        int num = Selection.objects.Length;
        if (num <= 0)
        {
            Debug.LogError("请选中需要合并的数字图片");
            return;
        }

        Dictionary<string, object> cfgDic = null;
        if (characterCfg != null)
        {
            cfgDic = MiniJSON.Json.Deserialize(characterCfg.text) as Dictionary<string, object>;
        }

        for (int i = 0; i < num; ++i)
        {
            Texture2D tex = Selection.objects[i] as Texture2D;
            string name = Path.GetFileNameWithoutExtension(tex.name);
            if (cfgDic != null && cfgDic.ContainsKey(name))
                ac.Add((int)(cfgDic[name].ToString()[0]));
            else
                ac.Add((int)(name[0]));
        }
        Texture2D r = new Texture2D(2, 2, TextureFormat.RGBA32, false);

        Texture2D[] textures = new Texture2D[num];

        for (int i = 0; i < num; i++)
        {
            Texture2D tex = Selection.objects[i] as Texture2D;
            textures[i] = tex;
        }
        rects = r.PackTextures(textures, 1);
        r.Apply();

        SaveTexture(r, texSavePath);
    }

    void SaveTexture(Texture2D texture, string path)
    {
        if (texture == null) return;
        File.WriteAllBytes(path, texture.EncodeToPNG());
        AssetDatabase.ImportAsset(path);
        AssetDatabase.Refresh();
    }

    [MenuItem("CrisTools/UI/美术字体", false, 1102)]
    public static void OpenWin()
    {
        EditorWindow.GetWindow<CrisFontWindow>("美术字体");
    }
}

制作美术字体

1 资源

1.1 资源方式

美术提供所有字的单个图片,大小一致
请添加图片描述
重点注意:单个图片必须打开读写和关闭压缩,否则会出现打包失败或打包卡死的情况。
在这里插入图片描述

1.2 资源命名

图片名字使用图片对应的字,比如:表示“1”的图片命名就为1,表示“+”的图片命名就为+
请添加图片描述

1.3 资源命名配置

如果出现图片表示的字不能当做文件名时,则可以使用json配置。
比如表示“%”的图片文件名可以叫做“baifenhao”,“@”的图片叫做“at”,在json配置中
{
“baifenhao”:“%”,
“at”:“@”
}
请添加图片描述

2 打包字体

2.1 创建字体

如果需创建一个新字体,输入创建字体的路径即可。
请添加图片描述
如下,我们在测试路径下生成了一个font字体文件。
请添加图片描述

2.2 更新字体

当创建了一个新字体,或者将一个已有的字体拖到字体框中后,选中碎片资源,就可以对字体进行打包更新了。
注:如有特殊字符,使用了1.3中json配置,则将对应json拖至字体配置选框中。
请添加图片描述
更新过后,就能得到字体所需的图集和材质了。
请添加图片描述

3 字体使用

请添加图片描述
在支持富文本组件的font处选择刚生成的字体文件即可正常使用。

注:笔者的富文本组件包含内容过于繁多,在此就不贴出了。如果有需要且不知道富本文组件怎么制作的,可以使用hub上大佬们写的富文本组件。(https://github.com/wuxiongbin/uHyperText)

猜你喜欢

转载自blog.csdn.net/CrisFu/article/details/124064784