服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Unity制作自定义字体的两种方法

Unity制作自定义字体的两种方法

2022-10-25 13:07jince1991 C#

这篇文章主要为大家详细介绍了Unity制作自定义字体的两种方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Unity支持自定义图片字体(CustomFont),网上有很多教程,细节不尽相同,当概括起来基本就是两种方式。一是使用BMFont,导出图集和.fnt文件,再使用图集在Unity中设置得到字体。二是不用BMFont,使用Unity自带的Sprite类似图集的功能。两种方式原理相同,只是手段有区别。基本原理都是先有一张贴图,比如:

Unity制作自定义字体的两种方法

需要知道的信息是贴图中每一个字符对应的ASCII码(例如0的ASCII码为48)与该字符在图集中对应的位置(0为x:0;y:0;w:55;h:76)。然后在Unity中创建材质和CustomFont并根据信息进行设置。

Unity制作自定义字体的两种方法

最后得到字体。

Unity制作自定义字体的两种方法

两种方式的区别仅在于第一步中如何得到图集的信息。具体的:

对于第一种使用BMFont的方式,目的是得到.fnt文件,实际上是xml格式文件。具体的信息为:

Unity制作自定义字体的两种方法

BMFont的使用方法不再详述。得到图集个fnt文件后,网上一般的方法是手动计算在Unity中的参数,有些繁琐,在这里写一个Editor脚本来自动完成这个过程。直接上代码:

  1. using System; 
  2. using System.Collections.Generic; 
  3. using System.IO; 
  4. using System.Xml; 
  5. using UnityEditor; 
  6. using UnityEngine; 
  7.   
  8. public class CreateFontFromFnt : EditorWindow 
  9.     [MenuItem("Tools/创建字体(Fnt)")] 
  10.     static void DoIt() 
  11.     { 
  12.         GetWindow<CreateFontFromFnt>("创建字体"); 
  13.     } 
  14.     private string fontName; 
  15.     private string fontPath; 
  16.     private Texture2D tex; 
  17.     private string fntFilePath; 
  18.   
  19.     private void OnGUI() 
  20.     { 
  21.         GUILayout.BeginVertical(); 
  22.   
  23.         GUILayout.BeginHorizontal(); 
  24.         GUILayout.Label("字体名称:"); 
  25.         fontName = EditorGUILayout.TextField(fontName); 
  26.         GUILayout.EndHorizontal(); 
  27.   
  28.         GUILayout.BeginHorizontal(); 
  29.         GUILayout.Label("字体图片:"); 
  30.         tex = (Texture2D)EditorGUILayout.ObjectField(tex, typeof(Texture2D), true); 
  31.         GUILayout.EndHorizontal(); 
  32.   
  33.         GUILayout.BeginHorizontal(); 
  34.         if (GUILayout.Button(string.IsNullOrEmpty(fontPath) ? "选择路径" : fontPath)) 
  35.         { 
  36.             fontPath = EditorUtility.OpenFolderPanel("字体路径", Application.dataPath, ""); 
  37.             if (string.IsNullOrEmpty(fontPath)) 
  38.             { 
  39.                 Debug.Log("取消选择路径"); 
  40.             } 
  41.             else 
  42.             { 
  43.                 fontPath = fontPath.Replace(Application.dataPath, "") + "/"
  44.             } 
  45.         } 
  46.         GUILayout.EndHorizontal(); 
  47.   
  48.         GUILayout.BeginHorizontal(); 
  49.         if (GUILayout.Button(string.IsNullOrEmpty(fntFilePath) ? "选择fnt文件" : fntFilePath)) 
  50.         { 
  51.             fntFilePath = EditorUtility.OpenFilePanelWithFilters("选择fnt文件", Environment.GetFolderPath(Environment.SpecialFolder.Desktop), new string[] { """fnt" }); 
  52.             if (string.IsNullOrEmpty(fntFilePath)) 
  53.             { 
  54.                 Debug.Log("取消选择路径"); 
  55.             } 
  56.         } 
  57.         GUILayout.EndHorizontal(); 
  58.   
  59.         GUILayout.BeginHorizontal(); 
  60.         if (GUILayout.Button("创建")) 
  61.         { 
  62.             Create(); 
  63.         } 
  64.         GUILayout.EndHorizontal(); 
  65.   
  66.         GUILayout.EndVertical(); 
  67.     } 
  68.   
  69.     private void Create() 
  70.     { 
  71.         if (string.IsNullOrEmpty(fntFilePath)) 
  72.         { 
  73.             Debug.LogError("fnt为空"); 
  74.             return
  75.         } 
  76.         if (tex == null
  77.         { 
  78.             Debug.LogError("字体图片为空"); 
  79.             return
  80.         } 
  81.   
  82.         string fontSettingPath = fontPath + fontName + ".fontsettings"
  83.         string matPath = fontPath + fontName + ".mat"
  84.         if (File.Exists(Application.dataPath + fontSettingPath)) 
  85.         { 
  86.             Debug.LogErrorFormat("已存在同名字体文件:{0}", fontSettingPath); 
  87.             return
  88.         } 
  89.         if (File.Exists(Application.dataPath + matPath)) 
  90.         { 
  91.             Debug.LogErrorFormat("已存在同名字体材质:{0}", matPath); 
  92.             return
  93.         } 
  94.         var list = new List<CharacterInfo>(); 
  95.         XmlDocument xmlDoc = new XmlDocument(); 
  96.         var content = File.ReadAllText(fntFilePath, System.Text.Encoding.UTF8); 
  97.         xmlDoc.LoadXml(content); 
  98.         var nodelist = xmlDoc.SelectNodes("font/chars/char"); 
  99.         foreach (XmlElement item in nodelist) 
  100.         { 
  101.             CharacterInfo info = new CharacterInfo(); 
  102.             var id = int.Parse(item.GetAttribute("id")); 
  103.             var x = float.Parse(item.GetAttribute("x")); 
  104.             var y = float.Parse(item.GetAttribute("y")); 
  105.             var width = float.Parse(item.GetAttribute("width")); 
  106.             var height = float.Parse(item.GetAttribute("height")); 
  107.   
  108.             info.index = id; 
  109.             //纹理映射,上下翻转 
  110.             info.uvBottomLeft = new Vector2(x / tex.width, 1 - (y + height) / tex.height); 
  111.             info.uvBottomRight = new Vector2((x + width) / tex.width, 1 - (y + height) / tex.height); 
  112.             info.uvTopLeft = new Vector2(x / tex.width, 1 - y / tex.height); 
  113.             info.uvTopRight = new Vector2((x + width) / tex.width, 1 - y / tex.height); 
  114.   
  115.             info.minX = 0; 
  116.             info.maxX = (int)width; 
  117.             info.minY = -(int)height / 2; 
  118.             info.maxY = (int)height / 2; 
  119.             info.advance = (int)width; 
  120.   
  121.             list.Add(info); 
  122.         } 
  123.   
  124.         Material mat = new Material(Shader.Find("GUI/Text Shader")); 
  125.         mat.SetTexture("_MainTex", tex); 
  126.         Font m_myFont = new Font(); 
  127.         m_myFont.material = mat; 
  128.         AssetDatabase.CreateAsset(mat, "Assets" + matPath); 
  129.         AssetDatabase.CreateAsset(m_myFont, "Assets" + fontSettingPath); 
  130.         m_myFont.characterInfo = list.ToArray(); 
  131.         EditorUtility.SetDirty(m_myFont); 
  132.         AssetDatabase.SaveAssets(); 
  133.         AssetDatabase.Refresh(); 
  134.         Debug.Log("创建成功!"); 
  135.     } 

使用起来很简单:

Unity制作自定义字体的两种方法

代码也没什么可深究的,目的是代替手动计算,只是在纹理映射的时候有一个小坑。

第二种方式使用Unity中的Sprite。Unity支持把一个Sprite切割成多个。可以用这种方式代替BMFont导出的fnt文件。需要手动做的工作是将图集的TextureType设置为Sprite,然后把SpriteMode设为Multiple,打开SpriteEditor,对图片进行切割。Slice就基本可以完成这个工作,如果需要再手动微调一下。

Unity制作自定义字体的两种方法

一张图按照字符的位置分割成了10个Sprite。然后选中每一个Sprite把Name设置成字符对应的ASCII码。这样第一种方法里fnt文件包含的信息基本都包含在这个“图集”里了。同样写一个Editor脚本把这些信息写入到CustomFont里面,并不用手动去创建。

  1. using UnityEngine; 
  2. using UnityEditor; 
  3. using System.IO; 
  4.   
  5. public class CreateFont : EditorWindow 
  6.     [MenuItem("Tools/创建字体(sprite)")] 
  7.     public static void Open() 
  8.     { 
  9.         GetWindow<CreateFont>("创建字体"); 
  10.     } 
  11.   
  12.     private Texture2D tex; 
  13.     private string fontName; 
  14.     private string fontPath; 
  15.   
  16.     private void OnGUI() 
  17.     { 
  18.         GUILayout.BeginVertical(); 
  19.   
  20.         GUILayout.BeginHorizontal(); 
  21.         GUILayout.Label("字体图片:"); 
  22.         tex = (Texture2D)EditorGUILayout.ObjectField(tex, typeof(Texture2D), true); 
  23.         GUILayout.EndHorizontal(); 
  24.   
  25.         GUILayout.BeginHorizontal(); 
  26.         GUILayout.Label("字体名称:"); 
  27.         fontName = EditorGUILayout.TextField(fontName); 
  28.         GUILayout.EndHorizontal(); 
  29.   
  30.         GUILayout.BeginHorizontal(); 
  31.         if (GUILayout.Button(string.IsNullOrEmpty(fontPath) ? "选择路径" : fontPath)) 
  32.         { 
  33.             fontPath = EditorUtility.OpenFolderPanel("字体路径", Application.dataPath, ""); 
  34.             if (string.IsNullOrEmpty(fontPath)) 
  35.             { 
  36.                 Debug.Log("取消选择路径"); 
  37.             } 
  38.             else 
  39.             { 
  40.                 fontPath = fontPath.Replace(Application.dataPath, "") + "/"
  41.             } 
  42.         } 
  43.         GUILayout.EndHorizontal(); 
  44.   
  45.         GUILayout.BeginHorizontal(); 
  46.         if (GUILayout.Button("创建")) 
  47.         { 
  48.             Create(); 
  49.         } 
  50.         GUILayout.EndHorizontal(); 
  51.   
  52.         GUILayout.EndVertical(); 
  53.     } 
  54.   
  55.     private void Create() 
  56.     { 
  57.         if (tex == null
  58.         { 
  59.             Debug.LogWarning("创建失败,图片为空!"); 
  60.             return
  61.         } 
  62.   
  63.         if (string.IsNullOrEmpty(fontPath)) 
  64.         { 
  65.             Debug.LogWarning("字体路径为空!"); 
  66.             return
  67.         } 
  68.         if (fontName == null
  69.         { 
  70.             Debug.LogWarning("创建失败,字体名称为空!"); 
  71.             return
  72.         } 
  73.         else 
  74.         { 
  75.             if (File.Exists(Application.dataPath + fontPath + fontName + ".fontsettings")) 
  76.             { 
  77.                 Debug.LogError("创建失败,已存在同名字体文件"); 
  78.                 return
  79.             } 
  80.             if (File.Exists(Application.dataPath + fontPath + fontName + ".mat")) 
  81.             { 
  82.                 Debug.LogError("创建失败,已存在同名字体材质文件"); 
  83.                 return
  84.             } 
  85.         } 
  86.   
  87.         string selectionPath = AssetDatabase.GetAssetPath(tex); 
  88.         if (selectionPath.Contains("/Resources/")) 
  89.         { 
  90.             string selectionExt = Path.GetExtension(selectionPath); 
  91.             if (selectionExt.Length == 0) 
  92.             { 
  93.                 Debug.LogError("创建失败!"); 
  94.                 return
  95.             } 
  96.             
  97.             string fontPathName = fontPath + fontName + ".fontsettings"
  98.             string matPathName = fontPath + fontName + ".mat"
  99.             float lineSpace = 0.1f; 
  100.             //string loadPath = selectionPath.Remove(selectionPath.Length - selectionExt.Length).Replace("Assets/Resources/", ""); 
  101.             string loadPath = selectionPath.Replace(selectionExt, "").Substring(selectionPath.IndexOf("/Resources/") + "/Resources/".Length); 
  102.             Sprite[] sprites = Resources.LoadAll<Sprite>(loadPath); 
  103.             if (sprites.Length > 0) 
  104.             { 
  105.                 Material mat = new Material(Shader.Find("GUI/Text Shader")); 
  106.                 mat.SetTexture("_MainTex", tex); 
  107.                 Font m_myFont = new Font(); 
  108.                 m_myFont.material = mat; 
  109.                 CharacterInfo[] characterInfo = new CharacterInfo[sprites.Length]; 
  110.                 for (int i = 0; i < sprites.Length; i++) 
  111.                 { 
  112.                     if (sprites[i].rect.height > lineSpace) 
  113.                     { 
  114.                         lineSpace = sprites[i].rect.height; 
  115.                     } 
  116.                 } 
  117.                 for (int i = 0; i < sprites.Length; i++) 
  118.                 { 
  119.                     Sprite spr = sprites[i]; 
  120.                     CharacterInfo info = new CharacterInfo(); 
  121.                     try 
  122.                     { 
  123.                         info.index = System.Convert.ToInt32(spr.name); 
  124.                     } 
  125.                     catch 
  126.                     { 
  127.                         Debug.LogError("创建失败,Sprite名称错误!"); 
  128.                         return
  129.                     } 
  130.                     Rect rect = spr.rect; 
  131.                     float pivot = spr.pivot.y / rect.height - 0.5f; 
  132.                     if (pivot > 0) 
  133.                     { 
  134.                         pivot = -lineSpace / 2 - spr.pivot.y; 
  135.                     } 
  136.                     else if (pivot < 0) 
  137.                     { 
  138.                         pivot = -lineSpace / 2 + rect.height - spr.pivot.y; 
  139.                     } 
  140.                     else 
  141.                     { 
  142.                         pivot = -lineSpace / 2; 
  143.                     } 
  144.                     int offsetY = (int)(pivot + (lineSpace - rect.height) / 2); 
  145.                     info.uvBottomLeft = new Vector2((float)rect.x / tex.width, (float)(rect.y) / tex.height); 
  146.                     info.uvBottomRight = new Vector2((float)(rect.x + rect.width) / tex.width, (float)(rect.y) / tex.height); 
  147.                     info.uvTopLeft = new Vector2((float)rect.x / tex.width, (float)(rect.y + rect.height) / tex.height); 
  148.                     info.uvTopRight = new Vector2((float)(rect.x + rect.width) / tex.width, (float)(rect.y + rect.height) / tex.height); 
  149.                     info.minX = 0; 
  150.                     info.minY = -(int)rect.height - offsetY; 
  151.                     info.maxX = (int)rect.width; 
  152.                     info.maxY = -offsetY; 
  153.                     info.advance = (int)rect.width; 
  154.                     characterInfo[i] = info; 
  155.                 } 
  156.                 AssetDatabase.CreateAsset(mat, "Assets" + matPathName); 
  157.                 AssetDatabase.CreateAsset(m_myFont, "Assets" + fontPathName); 
  158.                 m_myFont.characterInfo = characterInfo; 
  159.                 EditorUtility.SetDirty(m_myFont); 
  160.                 AssetDatabase.SaveAssets(); 
  161.                 AssetDatabase.Refresh();//刷新资源 
  162.                 Debug.Log("创建字体成功"); 
  163.   
  164.             } 
  165.             else 
  166.             { 
  167.                 Debug.LogError("图集错误!"); 
  168.             } 
  169.         } 
  170.         else 
  171.         { 
  172.             Debug.LogError("创建失败,选择的图片不在Resources文件夹内!"); 
  173.         } 
  174.     } 

这个脚本参考了某一篇博文,时间长了实在是找不到了。

原理跟第一种方法一样,只是计算细节略有差异。使用起来还是很简单:

Unity制作自定义字体的两种方法

大同小异的两种方法,个人更喜欢用第二种。不需要使用额外的软件,一键搞定,基本上可以丢给美术童鞋来做了。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:https://blog.csdn.net/u012662020/article/details/79996686

延伸 · 阅读

精彩推荐
  • C#Unity Shader实现素描效果

    Unity Shader实现素描效果

    这篇文章主要为大家详细介绍了Unity Shader实现素描效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    ZzEeRO6882022-09-06
  • C#C#巧用DateTime预设可选的日期范围(如本年度、本季度、本月等)

    C#巧用DateTime预设可选的日期范围(如本年度、本季度、本月等)

    这篇文章主要介绍了C#巧用DateTime预设可选的日期范围,如本年度、本季度、本月等,感兴趣的小伙伴们可以参考一下...

    C#教程网4672021-11-19
  • C#C#文件上传与下载的实现方法

    C#文件上传与下载的实现方法

    这篇文章主要为大家详细介绍了C#文件上传与下载的实现方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    小倔驴4782022-01-20
  • C#C#简单查询SQLite数据库是否存在数据的方法

    C#简单查询SQLite数据库是否存在数据的方法

    这篇文章主要介绍了C#简单查询SQLite数据库是否存在数据的方法,涉及C#调用SQLite组件及针对SQLite数据库基本的连接、查询、关闭等使用技巧,需要的朋友可以...

    IT部落格9412021-11-29
  • C#Unity实现大转盘的简单笔记

    Unity实现大转盘的简单笔记

    这篇文章主要为大家分享了Unity实现大转盘的简单笔记,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    MiKiNuo6732022-03-10
  • C#Unity时间戳的使用方法

    Unity时间戳的使用方法

    这篇文章主要为大家详细介绍了Unity时间戳的使用方法,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    起个名字真的好难啊12002022-09-08
  • C#C# byte转为有符号整数实例

    C# byte转为有符号整数实例

    这篇文章主要介绍了C# byte转为有符号整数实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...

    小鹰信息技术服务部3742022-10-14
  • C#c#读取XML多级子节点

    c#读取XML多级子节点

    本文主要介绍了c#读取XML多级子节点的方法。具有很好的参考价值。下面跟着小编一起来看下吧...

    gl博越11832021-12-30