UGUI使用BMFont制作美术字体<一>
不多说,先来效果图:
从头开始讲开发流程:
在Unity3d开发过程中,经常需要将美术提供的美术字组合成一个字体库,方便unity中的调用,BMFont则为此提供了不错的功能支持,它的下载地址在这里。它的使用方法网上有很多教程,这里不做解释,如果要使用此工具,要注意的是,这里记得使用xml格式,导出的图片为一张。
导出来的资源有:对应的图片,还有一个以fnt结尾的文件,如果打开此文件可以看到它就是一个xml文件:
在BMFont软件中我们这样操作:Options->Save configuration as...,把这个文件保存下来,它是以bmfc结尾的,保存了在bmfont里面相关的配置属性,其中对应的文字图片还有对应的值在imported icon images中保存,用记事本打开,就可以可到这样的界面:
以我的直觉,BMFont就是根据这样的一个配置文件生成对应的图片和xml文件。现在回到Unity,将BMFont生成的xml和图片分别拖动到1,2位置,点击Generate Font在对应的xml目录下便会生成字体文件和材质球,并且分别已经赋值,将字体拖动到ugui中的字体位置可见:
这样一个工具算是完成了,它的代码在这里,自己也可以进行修改。但是作为懒癌的我,觉得这样极不方便,制作字体时候,每次都要打开bmfont,然后导入unity,再接着将文件拖动到相关位置,还要点击生成字体,简直不能忍受,那么下一篇我们制作一个更加方便的工具。
using System.Collections.Generic; using System.IO; using System.Xml; using UnityEditor; using UnityEngine; public class BMFont : EditorWindow { private TextAsset _fontTextAsset; private Texture _fontTexture; private string _fontsDir; [MenuItem("CTools/BMFont", false, 12)] private static void BMFontTools() { BMFont bmFont = new BMFont(); bmFont.Show(); } private string _getAssetPath(string path) { string pathTemp = path.Replace("\\", "/"); pathTemp = pathTemp.Replace(Application.dataPath, "Assets"); return pathTemp; } void OnGUI() { EditorGUILayout.BeginVertical(); TextAsset taTemp = EditorGUILayout.ObjectField("选择Font文件:", _fontTextAsset, typeof(TextAsset), true) as TextAsset; if (taTemp != _fontTextAsset && taTemp != null) { string assetDir = Directory.GetParent(AssetDatabase.GetAssetPath(taTemp)).FullName; assetDir = _getAssetPath(assetDir); string imgPath = string.Format("{0}/{1}_0.png", assetDir, taTemp.name); _fontTexture = AssetDatabase.LoadAssetAtPath<Texture>(imgPath); _fontsDir = string.Format("{0}.fontsettings", Path.Combine(assetDir, taTemp.name)); if (_fontTexture == null) { _fontsDir = string.Empty; Debug.LogError(string.Format("未发现{0}文件", imgPath)); } } _fontTextAsset = taTemp; _fontTexture = EditorGUILayout.ObjectField("选择Font图片文件:", _fontTexture, typeof(Texture), true) as Texture; GUI.enabled = false; _fontsDir = EditorGUILayout.TextField("字体生成路径:", _fontsDir); GUI.enabled = true; if (GUILayout.Button("Generate Font")) { if (!string.IsNullOrEmpty(_fontsDir)) { Material mat = AssetDatabase.LoadAssetAtPath<Material>(_fontsDir.Replace(".fontsettings", ".mat")); if (mat == null) { mat = new Material(Shader.Find("UI/Default Font")); AssetDatabase.CreateAsset(mat, _fontsDir.Replace(".fontsettings", ".mat")); } if (_fontTexture != null) { mat = AssetDatabase.LoadAssetAtPath<Material>(_fontsDir.Replace(".fontsettings", ".mat")); mat.SetTexture("_MainTex", _fontTexture); } else { Debug.LogError("贴图未做配置,请检查配置"); return; } Font font = AssetDatabase.LoadAssetAtPath<Font>(_fontsDir); if (font == null) { font = new Font(); AssetDatabase.CreateAsset(font, _fontsDir); } _setFontInfo(AssetDatabase.LoadAssetAtPath<Font>(_fontsDir), AssetDatabase.GetAssetPath(_fontTextAsset), _fontTexture); font = AssetDatabase.LoadAssetAtPath<Font>(_fontsDir); font.material = mat; } else { Debug.LogError("创建失败,请检查配置"); } } EditorGUILayout.EndVertical(); } private void _setFontInfo(Font font, string fontConfig, Texture texture) { XmlDocument xml = new XmlDocument(); xml.Load(fontConfig); List<CharacterInfo> chtInfoList = new List<CharacterInfo>(); XmlNode node = xml.SelectSingleNode("font/chars"); foreach (XmlNode nd in node.ChildNodes) { XmlElement xe = (XmlElement)nd; int x = int.Parse(xe.GetAttribute("x")); int y = int.Parse(xe.GetAttribute("y")); int width = int.Parse(xe.GetAttribute("width")); int height = int.Parse(xe.GetAttribute("height")); int advance = int.Parse(xe.GetAttribute("xadvance")); CharacterInfo info = new CharacterInfo(); info.glyphHeight = texture.height; info.glyphWidth = texture.width; info.index = int.Parse(xe.GetAttribute("id")); //这里注意下UV坐标系和从BMFont里得到的信息的坐标系是不一样的哦,前者左下角为(0,0), //右上角为(1,1)。而后者则是左上角上角为(0,0),右下角为(图宽,图高) info.uvTopLeft = new Vector2((float)x / texture.width, 1 - (float)y / texture.height); info.uvTopRight = new Vector2((float)(x + width) / texture.width, 1 - (float)y / texture.height); info.uvBottomLeft = new Vector2((float)x / texture.width, 1 - (float)(y + height) / texture.height); info.uvBottomRight = new Vector2((float)(x + width) / texture.width, 1 - (float)(y + height) / texture.height); info.minX = 0; info.minY = -height; info.maxX = width; info.maxY = 0; info.advance = advance; chtInfoList.Add(info); } font.characterInfo = chtInfoList.ToArray(); AssetDatabase.Refresh(); } }
参考:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构