使用图集Atlas创建Fnt文件的工具

fnt文件生成unity字体的原理其实就是渲染图集Atlas上的Sprite,这边直接利用Unity自带的图集工具生成fnt文件

注意:这里生成的fnt文件还没法直接用,因为没有关联字符,这个工具只是第1步,第2步要用Fnt编辑工具关联字符:Fnt文件编辑工具

 

效果

 

public class SpriteToFntTool : EditorWindow
{

    [MenuItem("MyTools/Sprite To Fnt", false, 100)]
    private static void ShowWindow()
    {
        var wnd = GetWindow<SpriteToFntTool>(false, "SpriteToFnt");
    }

    private Vector2 m_ScrollPos = new Vector2(0, 0);
    private GUIContent m_TempLabelContent = new GUIContent();
    private Sprite m_Sprite;

    private void OnGUI()
    {
        GUILayout.Space(6);
        float labelWidthBak = EditorGUIUtility.labelWidth;

        if (null == m_Sprite)
            EditorGUILayout.HelpBox($"请先选择一个设置了Packing Tag的Sprite", MessageType.Warning);
        
        EditorGUILayout.BeginHorizontal();
        m_TempLabelContent.text = "Sprite: ";
        EditorGUIUtility.labelWidth = GUI.skin.label.CalcSize(m_TempLabelContent).x + 2;
        EditorGUILayout.PrefixLabel("Sprite:");

        m_Sprite = EditorGUILayout.ObjectField(m_Sprite, typeof(Sprite), false) as Sprite;
        EditorGUILayout.EndHorizontal();

        if (null != m_Sprite)
        {
            if (GUILayout.Button("刷新Packer图集"))
            {
                EditorUtility.DisplayProgressBar("RebuildAtlasCacheIfNeeded...", "RebuildAtlasCacheIfNeeded...", 0);
                Packer.RebuildAtlasCacheIfNeeded(EditorUserBuildSettings.activeBuildTarget, true, Packer.Execution.ForceRegroup);
                EditorUtility.ClearProgressBar();
            }
            Packer.GetAtlasDataForSprite(m_Sprite, out var packTag, out var atlasTex);

            var spFilePath = AssetDatabase.GetAssetPath(m_Sprite);
            var spFolderPath = Path.GetDirectoryName(spFilePath);
            if (null != atlasTex)
            {
                EditorGUILayout.LabelField($"所选Sprite的Packing Tag: {packTag}");
                EditorGUILayout.LabelField($"Sprite所在文件夹:{spFolderPath}");
                EditorGUILayout.LabelField("图集贴图:");
                GUILayout.Label(atlasTex, GUILayout.MaxHeight(120));
                GUILayout.Space(10);
                if (!EditorApplication.isPlaying)
                {
                    EditorGUILayout.HelpBox("请先运行Editor, 非运行状态无法获取到Packer图集的精灵信息", MessageType.Warning);
                    GUI.enabled = false;
                }

                EditorGUILayout.BeginHorizontal();
                if (GUILayout.Button($"  生成{packTag}.fnt  "))
                {
                    CreateFntFile(packTag, atlasTex, spFolderPath);
                }
                GUI.enabled = true;
                GUILayout.Space(10);

                if (GUILayout.Button($"  生成{packTag}.png  "))
                {
                    var pngFileName = $"{packTag}.png";
                    var pngFilePath = Path.Combine(spFolderPath, pngFileName);
                    ExportTexture(pngFilePath, atlasTex);
                    AssetDatabase.ImportAsset(pngFilePath); //不存在则不需要导入
                    AssetDatabase.Refresh();
                }

                GUILayout.FlexibleSpace();
                EditorGUILayout.EndHorizontal();
            }
        }

        EditorGUIUtility.labelWidth = labelWidthBak;
        GUILayout.Space(6);
    }

    private static void CreateFntFile(string packingTag, Texture2D fntTex, string spFolderPath)
    {
        var fntFilePath = Path.Combine(spFolderPath, $"{packingTag}.fnt");
        if (File.Exists(fntFilePath))
        {
            if (!EditorUtility.DisplayDialog("文件已存在", $"是否覆盖{fntFilePath}?", "Yes", "No"))
                return;
        }

        Fnt fnt = new Fnt();
        fnt.texWidth = fntTex.width;
        fnt.texHeight = fntTex.height;
        fnt.file = $"{packingTag}.png";

        var pngFiles = Directory.GetFiles(spFolderPath, "*.png", SearchOption.TopDirectoryOnly);
        int charCnt = 0;
        int maxHeight = 0;
        foreach (var pngFile in pngFiles)
        {
            var pngIMP = AssetImporter.GetAtPath(pngFile) as TextureImporter;
            if (pngIMP.spritePackingTag != packingTag)
                continue;

            var sp = AssetDatabase.LoadAssetAtPath<Sprite>(pngFile);
            FntChar fntChar = new FntChar();
            fnt.charsList.Add(fntChar);

            var texRect = sp.textureRect;
            fntChar.w = (int)texRect.width;
            fntChar.h = (int)texRect.height;
            fntChar.x = (int)texRect.x;
            fntChar.y = fntTex.height - (int)texRect.y - fntChar.h; //fnt中的x, y为左上角, 以贴图左上角为(0, 0)
            maxHeight = Mathf.Max(maxHeight, fntChar.h);
            fntChar.xadv = fntChar.w;
            charCnt++;
        }

        fnt.fontSize = maxHeight;
        fnt.lineHeight = maxHeight;
        fnt.baseLine = (int)(maxHeight * 0.9f);

        using (StreamWriter sw = new StreamWriter(fntFilePath, false, Encoding.UTF8))
        {
            sw.WriteLine($"info face=\"Art\" size={fnt.fontSize}");
            sw.WriteLine($"common lineHeight={fnt.lineHeight} base={fnt.baseLine} scaleW={fnt.texWidth} scaleH={fnt.texHeight} pages=1");
            sw.WriteLine($"page id=0 file=\"{fnt.file}\"");
            sw.WriteLine($"chars count={fnt.charsList.Count}");
            foreach (var c in fnt.charsList)
            {
                sw.WriteLine($"char id={c.id} x={c.x} y={c.y} width={c.w} height={c.h} xoffset={c.xoff} yoffset={c.yoff} xadvance={c.xadv}");
            }
        }

        AssetDatabase.ImportAsset(fntFilePath);
        AssetDatabase.Refresh();
    }

    static void ExportTexture(string outFilePath, Texture tex)
    {
        var tempRT = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);
        Graphics.SetRenderTarget(tempRT);
        Graphics.Blit(tex, tempRT);

        var resultTex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
        resultTex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
        resultTex.Apply();

        var bytes = resultTex.EncodeToPNG();

        Graphics.SetRenderTarget(null);
        RenderTexture.ReleaseTemporary(tempRT);

        File.WriteAllBytes(outFilePath, bytes);
    }

}

 

public class Fnt
{
    public int fontSize = 22; //字体大小
    public int lineHeight = 22; //行高
    public int baseLine = 20; //基线位置
    public int texWidth = 512; //贴图宽度
    public int texHeight = 256; //贴图高度
    public string file = ""; //贴图名字

    public List<FntChar> charsList = new List<FntChar>();
}

public class FntChar
{
    public string ch = ""; //对应字符
    //unicode码
    public int id;

    //在贴图上的坐标(左上角), 相对贴图左上角
    public int x;
    public int y;
    //在贴图上的宽高
    public int w;
    public int h;

    //排版修正
    public int xoff;
    public int yoff;
    //排版宽度
    public int xadv;
}

 

posted @ 2024-04-29 22:17  yanghui01  阅读(4)  评论(0编辑  收藏  举报