Unity 4.6 Bitmap font?

Hi,
Is it possible to use Bitmap font with the new UGUI system? I am used to NGUI and the artists I work with are always using bitmap fonts for our games, but I can’t find a way to use a bitmap fonts with the new UGUI system, only dynamic fonts.
Thanks.

1 Like

I just tried switch the font from dynamic to ascii set for instance and was surprised to discover that the new UI system requires the font to be set to dynamic.

P.S. If you are looking for a solution that works with the new UI system as well as bitmap fonts or something even better, take a look at my signature. I tried to PM you but you have that turned off.

Well it doesn’t require the font to be set to dynamic, it requires that you set up your Text element to respect that it is NOT a dynamic font (use default font size, default styling). Bitmap fonts work when this is configured.

I have font.png and font.fnt. How to import in Unity, and set to Text font?

1753968–110941–font.zip (80.3 KB)

Tim - can you elaborate on this?

My team and I are trying to implement bitmap fonts exported from GlyphDesigner.

Since you are not planning on using Dynamic fonts, I suggest you take a look at Text Mesh Pro. There is an early beta available from the author that works with Unity 4.6 and when it comes to text, it is an amazing solution with improved text rendering and so much more flexibility over bitmap fonts with no performance impact. Adding a Stroke, Shadow, Bevel, Glow, etc comes down to simply changing material properties.

Here is a link to the asset store thread where you can see lots of examples and information about it or take a look at the video below.

EDIT: Is there a way to automate (using Editor) custom font settings using the .fnt file and the font atlas?

I wrote an editor script to generate usable bitmap fonts and based the parsing algorithm on a code snippet I found in the net. I’ve only tested this with Littera using XML format (.fnt)

This code was written in 4.6b20 and should work on the latest RC build.

HOW TO USE:
Right click on the exported .fnt file (make sure it’s in the same directory as the font atlas) then click on “Generate Bitmap Font”. This will create the .fontsettings file (Unity font) and a material for it.

After generating, make sure to set “Line Spacing” of the font asset to the target font size. Font API doesn’t let me modify it, so you need to do it manually :slight_smile:

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

public static class BitmapFontImporter {

    [MenuItem("Assets/Generate Bitmap Font")]
    public static void GenerateFont()
    {
        TextAsset selected = (TextAsset)Selection.activeObject;
        string rootPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(selected));

        Texture2D texture = AssetDatabase.LoadAssetAtPath(rootPath + "/" + selected.name + ".png", typeof(Texture2D)) as Texture2D;
        if (!texture) throw new UnityException("Texture2d asset doesn't exist for " + selected.name);

        string exportPath = rootPath + "/" + Path.GetFileNameWithoutExtension(selected.name);

        Work(selected, exportPath, texture);
    }


    private static void Work(TextAsset import, string exportPath, Texture2D texture)
    {
        if (!import) throw new UnityException(import.name + "is not a valid font-xml file");

        Font font = new Font();
      
        XmlDocument xml = new XmlDocument();
        xml.LoadXml(import.text);
      
        XmlNode info = xml.GetElementsByTagName("info")[0];
        XmlNode common = xml.GetElementsByTagName("common")[0];
        XmlNodeList chars = xml.GetElementsByTagName("chars") [0].ChildNodes;

        float texW = texture.width;
        float texH = texture.height;
      
        CharacterInfo[] charInfos = new CharacterInfo[chars.Count];
        Rect r;
      
        for (int i=0; i<chars.Count; i++)
        {
            XmlNode charNode = chars[i];
            CharacterInfo charInfo = new CharacterInfo();
          
            charInfo.index = ToInt(charNode, "id");
            charInfo.width = ToInt(charNode, "xadvance");
            charInfo.flipped = false;
          
            r = new Rect();
            r.x = ((float) ToInt(charNode, "x")) / texW;
            r.y = ((float) ToInt(charNode, "y")) / texH;
            r.width = ((float) ToInt(charNode, "width")) / texW;
            r.height = ((float) ToInt(charNode, "height")) / texH;
            r.y = 1f - r.y - r.height;
            charInfo.uv = r;
          
          
            r = new Rect();
            r.x = (float) ToInt(charNode, "xoffset");
            r.y = (float) ToInt(charNode, "yoffset");
            r.width = (float) ToInt(charNode, "width");
            r.height = (float) ToInt(charNode, "height");
            r.y = -r.y;
            r.height = -r.height;
            charInfo.vert = r;
          
            charInfos[i] = charInfo;
        }

        // Create material
        Shader shader = Shader.Find("UI/Default");
        Material material = new Material(shader);
        material.mainTexture = texture;
        AssetDatabase.CreateAsset(material, exportPath + ".mat");

        // Create font
        font.material = material;
        font.name = info.Attributes.GetNamedItem("face").InnerText;
        font.characterInfo = charInfos;
        AssetDatabase.CreateAsset(font, exportPath + ".fontsettings");
    }
  
    private static int ToInt(XmlNode node, string name)
    {
        return Convert.ToInt32(node.Attributes.GetNamedItem(name).InnerText);
    }
}
3 Likes

Hello Xylph,

Thank you for your script. I think it will help me. I try to use it with an xml file generated with shoebox. It is the same format you are using but it did not succeed having correct text. I cannot understand why but character are not correctly aligned. Even if the value of the xml file seems ok. I try to modified value of the generated found manually but did not succeed to understand which value should be ok. Perhaps the problem comes from my UI.Text.

I attached the png I am using and here is the xml file

<font>
  <info face="D:\Creative Cloud Files\Chapo\Fonts\distance-export" size="80" />
  <common lineHeight="80" scaleW="161" scaleH="164" pages="1" />
  <pages>
    <page id="0" file="D:\Creative Cloud Files\Chapo\Fonts\distance-export.png" />
  </pages>
  <chars count="13">
    <char id="48" x="0" y="101" width="40" height="54" xoffset="2" yoffset="26" xadvance="41" /><!-- 0 -->
    <char id="49" x="121" y="54" width="39" height="53" xoffset="2" yoffset="26" xadvance="40" /><!-- 1 -->
    <char id="50" x="121" y="0" width="39" height="53" xoffset="2" yoffset="26" xadvance="40" /><!-- 2 -->
    <char id="51" x="81" y="109" width="39" height="54" xoffset="2" yoffset="26" xadvance="40" /><!-- 3 -->
    <char id="52" x="81" y="54" width="39" height="54" xoffset="2" yoffset="26" xadvance="40" /><!-- 4 -->
    <char id="53" x="121" y="108" width="39" height="54" xoffset="2" yoffset="26" xadvance="40" /><!-- 5 -->
    <char id="54" x="41" y="98" width="39" height="56" xoffset="2" yoffset="24" xadvance="40" /><!-- 6 -->
    <char id="55" x="41" y="44" width="39" height="53" xoffset="2" yoffset="27" xadvance="40" /><!-- 7 -->
    <char id="56" x="81" y="0" width="39" height="53" xoffset="2" yoffset="27" xadvance="40" /><!-- 8 -->
    <char id="57" x="0" y="44" width="40" height="56" xoffset="2" yoffset="23" xadvance="41" /><!-- 9 -->
    <char id="109" x="0" y="0" width="44" height="43" xoffset="0" yoffset="37" xadvance="45" /><!-- m -->
    <char id="32" x="0" y="0" width="0" height="0" xoffset="0" yoffset="0" xadvance="20" /><!--   -->
    <char id="9" x="0" y="0" width="0" height="0" xoffset="0" yoffset="0" xadvance="160" /><!--      -->
  </chars>
  <kernings count="0">
  </kernings>
</font>

I also copy an example (as screenshot) of my problem.
My text only contains “0”
We can see the 0 but also part of 9,7,6.

So I also copy a screenshot of my Text component

I you have any ideas of what to test it will really help me

1888917--121530--distance-export.png 1888917--121531--Capture d’écran 2014-12-16 à 21.55.22.png

If found my problem : my texture was not power of two so unity3d put it to 256x256. I just need to correctly setup the texture so it keep its resolution.

Just to make an import easy check the new plugin - CJFinc: Bitmap font tools http://cjf.in.ua/post/106094357973/cjfinc-bitmap-font-tools

Also search for TYPOGENIC…

Hi, I’m not sure if this will help you but I ran into a similar problem with my other project (if I can remember it correctly). The fix I made with the script was to use floats instead of ints. Anyway here’s the edited script. The fix is kinda messy as I just hacked it as fast as I can to make it work (sorry XD)

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

public static class BitmapFontImporter {

    [MenuItem("Assets/Generate Bitmap Font")]
    public static void GenerateFont()
    {
        TextAsset selected = (TextAsset)Selection.activeObject;
        string rootPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(selected));

        Texture2D texture = AssetDatabase.LoadAssetAtPath(rootPath + "/" + selected.name + ".png", typeof(Texture2D)) as Texture2D;
        if (!texture) throw new UnityException("Texture2d asset doesn't exist for " + selected.name);

        string exportPath = rootPath + "/" + Path.GetFileNameWithoutExtension(selected.name);

        Work(selected, exportPath, texture);
    }


    private static void Work(TextAsset import, string exportPath, Texture2D texture)
    {
        if (!import) throw new UnityException(import.name + "is not a valid font-xml file");

        Font font = new Font();
      
        XmlDocument xml = new XmlDocument();
        xml.LoadXml(import.text);
      
        XmlNode info = xml.GetElementsByTagName("info")[0];
        XmlNode common = xml.GetElementsByTagName("common")[0];
        XmlNodeList chars = xml.GetElementsByTagName("chars") [0].ChildNodes;

        float texW = texture.width;
        float texH = texture.height;
      
        CharacterInfo[] charInfos = new CharacterInfo[chars.Count];
        Rect r;
      
        for (int i=0; i<chars.Count; i++)
        {
            XmlNode charNode = chars[i];
            CharacterInfo charInfo = new CharacterInfo();
          
            charInfo.index = (int)ToFloat(charNode, "id");
            charInfo.width = ToFloat(charNode, "xadvance");
            charInfo.flipped = false;
          
            r = new Rect();
            r.x = ((float) ToFloat(charNode, "x")) / texW;
            r.y = ((float) ToFloat(charNode, "y")) / texH;
            r.width = ((float) ToFloat(charNode, "width")) / texW;
            r.height = ((float) ToFloat(charNode, "height")) / texH;
            r.y = 1f - r.y - r.height;
            charInfo.uv = r;
          
          
            r = new Rect();
            r.x = (float) ToFloat(charNode, "xoffset");
            r.y = (float) ToFloat(charNode, "yoffset");
            r.width = (float) ToFloat(charNode, "width");
            r.height = (float) ToFloat(charNode, "height");
            r.y = -r.y;
            r.height = -r.height;
            charInfo.vert = r;
          
            charInfos[i] = charInfo;
        }

        // Create material
        Shader shader = Shader.Find("UI/Default");
        Material material = new Material(shader);
        material.mainTexture = texture;
        AssetDatabase.CreateAsset(material, exportPath + ".mat");

        // Create font
        font.material = material;
        font.name = info.Attributes.GetNamedItem("face").InnerText;
        font.characterInfo = charInfos;
        AssetDatabase.CreateAsset(font, exportPath + ".fontsettings");
    }
  
    private static float ToFloat(XmlNode node, string name)
    {
        return float.Parse(node.Attributes.GetNamedItem(name).InnerText);
    }
}

And yes I’m always using power of two for the texture. Just want to update you if it goes weird on other exports.

Thank you VERY much for this!

It works for me, but I was getting a NULL reference error on Shader.Find(“UI/Default”).

I had to change it to: Shader.Find(“Unlit/Transparent”).

Hello,

I would like to use your script but I got this error when I try to import the bitmap with a .fnt

NullReferenceException: Object reference not set to an instance of an object
BitmapFontImporter.ToFloat (System.Xml.XmlNode node, System.String name) (at BitmapFontImporter.cs:92)
BitmapFontImporter.Work (UnityEngine.TextAsset import, System.String exportPath, UnityEngine.Texture2D texture) (at BitmapFontImporter.cs:51)
BitmapFontImporter.GenerateFont () (at BitmapFontImporter.cs:23)

return float.Parse (node.Attributes.GetNamedItem (name).InnerText);

Ok I fixed the bug by adding one line:

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

public static class BitmapFontImporter
{
  
    [MenuItem("Assets/Generate Bitmap Font")]
    public static void GenerateFont ()
    {
        TextAsset selected = (TextAsset)Selection.activeObject;
        string rootPath = Path.GetDirectoryName (AssetDatabase.GetAssetPath (selected));
      
        Texture2D texture = AssetDatabase.LoadAssetAtPath (rootPath + "/" + selected.name + ".png", typeof(Texture2D)) as Texture2D;
        if (!texture)
            throw new UnityException ("Texture2d asset doesn't exist for " + selected.name);
      
        string exportPath = rootPath + "/" + Path.GetFileNameWithoutExtension (selected.name);
      
        Work (selected, exportPath, texture);
    }
  
  
    private static void Work (TextAsset import, string exportPath, Texture2D texture)
    {
        if (!import)
            throw new UnityException (import.name + "is not a valid font-xml file");
      
        Font font = new Font ();
      
        XmlDocument xml = new XmlDocument ();
        xml.LoadXml (import.text);
      
        XmlNode info = xml.GetElementsByTagName ("info") [0];
        XmlNode common = xml.GetElementsByTagName ("common") [0];
        XmlNodeList chars = xml.GetElementsByTagName ("chars") [0].ChildNodes;
      
        float texW = texture.width;
        float texH = texture.height;
      
        CharacterInfo[] charInfos = new CharacterInfo[chars.Count];
        Rect r;
      
        for (int i=0; i<chars.Count; i++) {
            XmlNode charNode = chars [i];
            if (charNode.Attributes != null) {
                CharacterInfo charInfo = new CharacterInfo ();
          
                charInfo.index = (int)ToFloat (charNode, "id");
                charInfo.width = ToFloat (charNode, "xadvance");
                charInfo.flipped = false;
          
                r = new Rect ();
                r.x = ((float)ToFloat (charNode, "x")) / texW;
                r.y = ((float)ToFloat (charNode, "y")) / texH;
                r.width = ((float)ToFloat (charNode, "width")) / texW;
                r.height = ((float)ToFloat (charNode, "height")) / texH;
                r.y = 1f - r.y - r.height;
                charInfo.uv = r;
          
          
                r = new Rect ();
                r.x = (float)ToFloat (charNode, "xoffset");
                r.y = (float)ToFloat (charNode, "yoffset");
                r.width = (float)ToFloat (charNode, "width");
                r.height = (float)ToFloat (charNode, "height");
                r.y = -r.y;
                r.height = -r.height;
                charInfo.vert = r;
          
                charInfos [i] = charInfo;
            }
        }
      
        // Create material
        Shader shader = Shader.Find ("UI/Default");
        Material material = new Material (shader);
        material.mainTexture = texture;
        AssetDatabase.CreateAsset (material, exportPath + ".mat");
      
        // Create font
        font.material = material;
        font.name = info.Attributes.GetNamedItem ("face").InnerText;
        font.characterInfo = charInfos;
        AssetDatabase.CreateAsset (font, exportPath + ".fontsettings");
    }
  
    private static float ToFloat (XmlNode node, string name)
    {
        return float.Parse (node.Attributes.GetNamedItem (name).InnerText);
    }
}

Hello,

I wrote a tutorial about generating and importing bitmap fonts for Unity with ShoeBox and GlyphDesigner.

Thanks for your

I create a GitHub repo. If you need to update the script do not hesitate the do a pull request :).

2 Likes