主页 > IT业界  > 

Unity位图字体

Unity位图字体
下载Bitmap Font Generator

BMFont - AngelCode

解压后不用安装直接双击使用

提前设置

1、设置Bit depth为32

Options->Export options

2、清空所选字符

因为我们将在后边导入需要的字符。

Edit->Select all chars  先选择所有字符

Edit->Clear all chars in font  再清空所有字符

配置字符

1、打开Open Image Manager

Edit->Open Image Manager

2、配置字符和对应的字符图片

先准备这些字符的ASCII码

可以通过在线工具查询:ASCII编码转换,ASCII码在线查询工具

字符

ASCII码

0

48

1

49

2

50

3

51

4

52

5

53

6

54

7

55

8

56

9

57

.

46

+

43

-

45

K

75

M

77

G

71

点击Image->Import image,开始配置

返回主界面,查看标有小亮点的字符为已配置成功的字符

导出文件

导出前可以预览一下 Options->Visualize

直接导出

Options->Save bitmap font as....

导出成功

导入Unity使用

创建下面两个脚本,将其放到Editor文件夹下

using UnityEngine; using UnityEditor; using System; using System.IO; public class BFImporter : AssetPostprocessor { static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { foreach (string str in importedAssets) { DoImportBitmapFont(str); } foreach (string str in deletedAssets) { DelBitmapFont(str); } for (var i = 0; i < movedAssets.Length; i++) { MoveBitmapFont(movedFromAssetPaths[i], movedAssets[i]); } } public static bool IsFnt(string path) { return path.EndsWith(".fnt", StringComparison.OrdinalIgnoreCase); } public static void DoImportBitmapFont(string fntPath) { if (!IsFnt(fntPath)) return; TextAsset fnt = AssetDatabase.LoadMainAssetAtPath(fntPath) as TextAsset; string text = fnt.text; FntParse parse = FntParse.GetFntParse(ref text); if (parse == null) return; string fntName = Path.GetFileNameWithoutExtension(fntPath); string rootPath = Path.GetDirectoryName(fntPath); string fontPath = string.Format("{0}/{1}.fontsettings", rootPath, fntName); Texture2D[] textures = DoImportTextures(parse, rootPath, fnt); if (textures.Length > 1) { Debug.LogError(fntPath + " has more than one texture!"); } Font font = AssetDatabase.LoadMainAssetAtPath(fontPath) as Font; if (font == null) { font = new Font(); AssetDatabase.CreateAsset(font, fontPath); AssetDatabase.WriteImportSettingsIfDirty(fontPath); AssetDatabase.ImportAsset(fontPath); } Material material = AssetDatabase.LoadAssetAtPath(fontPath, typeof(Material)) as Material; if (material == null) { material = new Material(Shader.Find("UI/Default")); material.name = "Font Material"; AssetDatabase.AddObjectToAsset(material, fontPath); // unity 5.4+ cannot refresh it immediately, must import it AssetDatabase.ImportAsset(fontPath); } font.material = material; material.mainTexture = textures[0]; font.characterInfo = parse.charInfos; SerializedObject so = new SerializedObject(font); so.Update(); so.FindProperty("m_FontSize").floatValue = Mathf.Abs(parse.fontSize); so.FindProperty("m_LineSpacing").floatValue = parse.lineHeight; so.FindProperty("m_Ascent").floatValue = parse.lineBaseHeight; SerializedProperty prop = so.FindProperty("m_Descent"); if (prop != null) prop.floatValue = parse.lineBaseHeight - parse.lineHeight; UpdateKernings(so, parse.kernings); so.ApplyModifiedProperties(); so.SetIsDifferentCacheDirty(); AssetDatabase.DeleteAsset(fntPath); AssetDatabase.SaveAssets(); // unity 5.5 can not load custom font ReloadFont(fontPath); } private static Texture2D[] DoImportTextures(FntParse parse, string rootPath, TextAsset fnt) { int len = parse.textureNames.Length; Texture2D[] textures = new Texture2D[len]; for (int i = 0; i < len; i++) { string texPath = string.Format("{0}/{1}", rootPath, parse.textureNames[i]); Texture2D texture = AssetDatabase.LoadMainAssetAtPath(texPath) as Texture2D; if (texture == null) { Debug.LogErrorFormat(fnt, "{0}: not found '{1}'.", typeof(BFImporter), texPath); return textures; } TextureImporter texImporter = AssetImporter.GetAtPath(texPath) as TextureImporter; texImporter.textureType = TextureImporterType.GUI; texImporter.mipmapEnabled = false; texImporter.SaveAndReimport(); textures[i] = texture; } return textures; } private static void UpdateKernings(SerializedObject so, Kerning[] kernings) { int len = kernings != null ? kernings.Length : 0; SerializedProperty kerningsProp = so.FindProperty("m_KerningValues"); if (len == 0) { kerningsProp.ClearArray(); return; } int propLen = kerningsProp.arraySize; for (int i = 0; i < len; i++) { if (propLen <= i) { kerningsProp.InsertArrayElementAtIndex(i); } SerializedProperty kerningProp = kerningsProp.GetArrayElementAtIndex(i); kerningProp.FindPropertyRelative("second").floatValue = kernings[i].amount; SerializedProperty pairProp = kerningProp.FindPropertyRelative("first"); pairProp.Next(true); pairProp.intValue = kernings[i].first; pairProp.Next(false); pairProp.intValue = kernings[i].second; } for (int i = propLen - 1; i >= len; i--) { kerningsProp.DeleteArrayElementAtIndex(i); } } private static void DelBitmapFont(string fntPath) { if (!IsFnt(fntPath)) return; string fontPath = fntPath.Substring(0, fntPath.Length - 4) + ".fontsettings"; AssetDatabase.DeleteAsset(fontPath); } private static void MoveBitmapFont(string oldFntPath, string nowFntPath) { if (!IsFnt(nowFntPath)) return; string oldFontPath = oldFntPath.Substring(0, oldFntPath.Length - 4) + ".fontsettings"; string nowFontPath = nowFntPath.Substring(0, nowFntPath.Length - 4) + ".fontsettings"; AssetDatabase.MoveAsset(oldFontPath, nowFontPath); } // new font can not display via Text in unity 5.5 // must import import it private static void ReloadFont(string fontPath) { var tmpPath = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); AssetDatabase.ExportPackage(fontPath, tmpPath); AssetDatabase.DeleteAsset(fontPath); var startTime = DateTime.Now; EditorApplication.CallbackFunction func = null; func = () => { TimeSpan dalt = DateTime.Now - startTime; if (dalt.TotalSeconds >= 0.1) { EditorApplication.update -= func; AssetDatabase.ImportPackage(tmpPath, false); File.Delete(tmpPath); } }; EditorApplication.update += func; } } using UnityEngine; using System.Xml; using System; using System.Collections.Generic; using System.Text.RegularExpressions; public struct Kerning { public int first; public int second; public int amount; } public class FntParse { public int textureWidth; public int textureHeight; public string[] textureNames; public string fontName; public int fontSize; public int lineHeight; public int lineBaseHeight; public CharacterInfo[] charInfos { get; private set; } public Kerning[] kernings { get; private set; } public static FntParse GetFntParse(ref string text) { FntParse parse = null; if (text.StartsWith("info")) { parse = new FntParse(); parse.DoTextParse(ref text); } else if (text.StartsWith("<")) { parse = new FntParse(); parse.DoXMLPase(ref text); } return parse; } #region xml public void DoXMLPase(ref string content) { XmlDocument xml = new XmlDocument(); xml.LoadXml(content); XmlNode info = xml.GetElementsByTagName("info")[0]; XmlNode common = xml.GetElementsByTagName("common")[0]; XmlNodeList pages = xml.GetElementsByTagName("pages")[0].ChildNodes; XmlNodeList chars = xml.GetElementsByTagName("chars")[0].ChildNodes; fontName = info.Attributes.GetNamedItem("face").InnerText; fontSize = ToInt(info, "size"); lineHeight = ToInt(common, "lineHeight"); lineBaseHeight = ToInt(common, "base"); textureWidth = ToInt(common, "scaleW"); textureHeight = ToInt(common, "scaleH"); int pageNum = ToInt(common, "pages"); textureNames = new string[pageNum]; for (int i = 0; i < pageNum; i++) { XmlNode page = pages[i]; int pageId = ToInt(page, "id"); textureNames[pageId] = page.Attributes.GetNamedItem("file").InnerText; } charInfos = new CharacterInfo[chars.Count]; for (int i = 0; i < chars.Count; i++) { XmlNode charNode = chars[i]; charInfos[i] = CreateCharInfo( ToInt(charNode, "id"), ToInt(charNode, "x"), ToInt(charNode, "y"), ToInt(charNode, "width"), ToInt(charNode, "height"), ToInt(charNode, "xoffset"), ToInt(charNode, "yoffset"), ToInt(charNode, "xadvance"), ToInt(charNode, "page")); } // kernings XmlNode kerningsNode = xml.GetElementsByTagName("kernings")[0]; if (kerningsNode != null && kerningsNode.HasChildNodes) { XmlNodeList kerns = kerningsNode.ChildNodes; kernings = new Kerning[kerns.Count]; for (int i = 0; i < kerns.Count; i++) { XmlNode kerningNode = kerns[i]; kernings[i] = new Kerning(); kernings[i].first = ToInt(kerningNode, "first"); kernings[i].second = ToInt(kerningNode, "second"); kernings[i].amount = ToInt(kerningNode, "amount"); } } } private static int ToInt(XmlNode node, string name) { return int.Parse(node.Attributes.GetNamedItem(name).InnerText); } #endregion #region text private Regex pattern; public void DoTextParse(ref string content) { // letter=" " // \S+=".+?" // letter="x" // \S+=".+?" // letter=""" // \S+=".+?" // letter="" // \S+ // char // \S+ pattern = new Regex(@"\S+="".+?""|\S+"); string[] lines = content.Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries); ReadTextInfo(ref lines[0]); ReadTextCommon(ref lines[1]); for (int j = 0; j < textureNames.Length; j++) { ReadTextPage(ref lines[j + 2]); } // don't use count of chars, count is incorrect if has space //ReadTextCharCount(ref lines[3]); List<CharacterInfo> list = new List<CharacterInfo>(); int i = 2 + textureNames.Length; int l = lines.Length; for (; i < l; i++) { if (!ReadTextChar(i - 4, ref lines[i], ref list)) break; } charInfos = list.ToArray(); // skip empty line for (; i < l; i++) { if (lines[i].Length > 0) break; } // kernings if (i < l) { int count = 0; if (ReadTextCount(ref lines[i++], out count)) { int start = i; kernings = new Kerning[count]; for (; i < l; i++) { if (!ReadTextKerning(i - start, ref lines[i], ref list)) break; } }; } } private void ReadTextInfo(ref string line) { string[] keys; string[] values; SplitParts(line, out keys, out values); for (int i = keys.Length - 1; i >= 0; i--) { switch (keys[i]) { case "face": fontName = values[i]; break; case "size": fontSize = int.Parse(values[i]); break; } } } private void ReadTextCommon(ref string line) { string[] keys; string[] values; SplitParts(line, out keys, out values); for (int i = keys.Length - 1; i >= 0; i--) { switch (keys[i]) { case "lineHeight": lineHeight = int.Parse(values[i]); break; case "base": lineBaseHeight = int.Parse(values[i]); break; case "scaleW": textureWidth = int.Parse(values[i]); break; case "scaleH": textureHeight = int.Parse(values[i]); break; case "pages": textureNames = new string[int.Parse(values[i])]; break; } } } private void ReadTextPage(ref string line) { string[] keys; string[] values; SplitParts(line, out keys, out values); string textureName = null; int pageId = -1; for (int i = keys.Length - 1; i >= 0; i--) { switch (keys[i]) { case "file": textureName = values[i]; break; case "id": pageId = int.Parse(values[i]); break; } } textureNames[pageId] = textureName; } private bool ReadTextCount(ref string line, out int count) { string[] keys; string[] values; SplitParts(line, out keys, out values); count = 0; for (int i = keys.Length - 1; i >= 0; i--) { switch (keys[i]) { case "count": count = int.Parse(values[i]); return true; } } return false; } private bool ReadTextChar(int idx, ref string line, ref List<CharacterInfo> list) { if (!line.StartsWith("char")) return false; string[] keys; string[] values; SplitParts(line, out keys, out values); int id = 0, x = 0, y = 0, w = 0, h = 0, xo = 0, yo = 0, xadvance = 0; for (int i = keys.Length - 1; i >= 0; i--) { switch (keys[i]) { case "id": id = int.Parse(values[i]); break; case "x": x = int.Parse(values[i]); break; case "y": y = int.Parse(values[i]); break; case "width": w = int.Parse(values[i]); break; case "height": h = int.Parse(values[i]); break; case "xoffset": xo = int.Parse(values[i]); break; case "yoffset": yo = int.Parse(values[i]); break; case "xadvance": xadvance = int.Parse(values[i]); break; } } list.Add(CreateCharInfo(id, x, y, w, h, xo, yo, xadvance)); return true; } private bool ReadTextKerning(int idx, ref string line, ref List<CharacterInfo> list) { if (!line.StartsWith("kerning")) return false; string[] keys; string[] values; SplitParts(line, out keys, out values); Kerning kerning = new Kerning(); for (int i = keys.Length - 1; i >= 0; i--) { switch (keys[i]) { case "first": kerning.first = int.Parse(values[i]); break; case "second": kerning.second = int.Parse(values[i]); break; case "amount": kerning.amount = int.Parse(values[i]); break; } } kernings[idx] = kerning; return true; } private bool SplitParts(string line, out string[] keys, out string[] values) { MatchCollection parts = pattern.Matches(line); int count = parts.Count; keys = new string[count - 1]; values = new string[count - 1]; for (int i = count - 2; i >= 0; i--) { string part = parts[i + 1].Value; int pos = part.IndexOf('='); keys[i] = part.Substring(0, pos); values[i] = part.Substring(pos + 1).Trim('"'); } return true; } #endregion private CharacterInfo CreateCharInfo(int id, int x, int y, int w, int h, int xo, int yo, int xadvance, int page = 0) { Rect uv = new Rect(); uv.x = (float)x / textureWidth + page; uv.y = (float)y / textureHeight; uv.width = (float)w / textureWidth; uv.height = (float)h / textureHeight; uv.y = 1f - uv.y - uv.height; Rect vert = new Rect(); vert.x = xo; #if UNITY_5_0 || UNITY_5_1 || UNITY_5_2 // unity 5.0 can not support baseline for vert.y = yo; #else vert.y = yo - lineBaseHeight; #endif vert.width = w; vert.height = h; vert.y = -vert.y; vert.height = -vert.height; CharacterInfo charInfo = new CharacterInfo(); charInfo.index = id; #if UNITY_5_3_OR_NEWER || UNITY_5_3 || UNITY_5_2 charInfo.uvBottomLeft = new Vector2(uv.xMin, uv.yMin); charInfo.uvBottomRight = new Vector2(uv.xMax, uv.yMin); charInfo.uvTopLeft = new Vector2(uv.xMin, uv.yMax); charInfo.uvTopRight = new Vector2(uv.xMax, uv.yMax); charInfo.minX = (int)vert.xMin; charInfo.maxX = (int)vert.xMax; charInfo.minY = (int)vert.yMax; charInfo.maxY = (int)vert.yMin; charInfo.bearing = (int)vert.x; charInfo.advance = xadvance; #else #pragma warning disable 618 charInfo.uv = uv; charInfo.vert = vert; charInfo.width = xadvance; #pragma warning restore 618 #endif return charInfo; } }

将这两个文件直接拖入Unity中,系统自动生成字体文件

直接使用

标签:

Unity位图字体由讯客互联IT业界栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“Unity位图字体