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

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

最后得到字体。

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

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

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

using System;

using System.Collections.Generic;

using System.IO;

using System.Xml;

using UnityEditor;

using UnityEngine;

public class CreateFontFromFnt : EditorWindow

{

[MenuItem("Tools/创建字体(Fnt)")]

static void DoIt()

{

GetWindow("创建字体");

}

private string fontName;

private string fontPath;

private Texture2D tex;

private string fntFilePath;

private void OnGUI()

{

GUILayout.BeginVertical();

GUILayout.BeginHorizontal();

GUILayout.Label("字体名称:");

fontName = EditorGUILayout.TextField(fontName);

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

GUILayout.Label("字体图片:");

tex = (Texture2D)EditorGUILayout.ObjectField(tex, typeof(Texture2D), true);

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

if (GUILayout.Button(string.IsNullOrEmpty(fontPath) ? "选择路径" : fontPath))

{

fontPath = EditorUtility.OpenFolderPanel("字体路径", Application.dataPath, "");

if (string.IsNullOrEmpty(fontPath))

{

Debug.Log("取消选择路径");

}

else

{

fontPath = fontPath.Replace(Application.dataPath, "") + "/";

}

}

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

if (GUILayout.Button(string.IsNullOrEmpty(fntFilePath) ? "选择fnt文件" : fntFilePath))

{

fntFilePath = EditorUtility.OpenFilePanelWithFilters("选择fnt文件", Environment.GetFolderPath(Environment.SpecialFolder.Desktop), new string[] { "", "fnt" });

if (string.IsNullOrEmpty(fntFilePath))

{

Debug.Log("取消选择路径");

}

}

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

if (GUILayout.Button("创建"))

{

Create();

}

GUILayout.EndHorizontal();

GUILayout.EndVertical();

}

private void Create()

{

if (string.IsNullOrEmpty(fntFilePath))

{

Debug.LogError("fnt为空");

return;

}

if (tex == null)

{

Debug.LogError("字体图片为空");

return;

}

string fontSettingPath = fontPath + fontName + ".fontsettings";

string matPath = fontPath + fontName + ".mat";

if (File.Exists(Application.dataPath + fontSettingPath))

{

Debug.LogErrorFormat("已存在同名字体文件:{0}", fontSettingPath);

return;

}

if (File.Exists(Application.dataPath + matPath))

{

Debug.LogErrorFormat("已存在同名字体材质:{0}", matPath);

return;

}

var list = new List();

XmlDocument xmlDoc = new XmlDocument();

var content = File.ReadAllText(fntFilePath, System.Text.Encoding.UTF8);

xmlDoc.LoadXml(content);

var nodelist = xmlDoc.SelectNodes("font/chars/char");

foreach (XmlElement item in nodelist)

{

CharacterInfo info = new CharacterInfo();

var id = int.Parse(item.GetAttribute("id"));

var x = float.Parse(item.GetAttribute("x"));

var y = float.Parse(item.GetAttribute("y"));

var width = float.Parse(item.GetAttribute("width"));

var height = float.Parse(item.GetAttribute("height"));

info.index = id;

//纹理映射,上下翻转

info.uvBottomLeft = new Vector2(x / tex.width, 1 - (y + height) / tex.height);

info.uvBottomRight = new Vector2((x + width) / tex.width, 1 - (y + height) / tex.height);

info.uvTopLeft = new Vector2(x / tex.width, 1 - y / tex.height);

info.uvTopRight = new Vector2((x + width) / tex.width, 1 - y / tex.height);

info.minX = 0;

info.maxX = (int)width;

info.minY = -(int)height / 2;

info.maxY = (int)height / 2;

info.advance = (int)width;

list.Add(info);

}

Material mat = new Material(Shader.Find("GUI/Text Shader"));

mat.SetTexture("_MainTex", tex);

Font m_myFont = new Font();

m_myFont.material = mat;

AssetDatabase.CreateAsset(mat, "Assets" + matPath);

AssetDatabase.CreateAsset(m_myFont, "Assets" + fontSettingPath);

m_myFont.characterInfo = list.ToArray();

EditorUtility.SetDirty(m_myFont);

AssetDatabase.SaveAssets();

AssetDatabase.Refresh();

Debug.Log("创建成功!");

}

}

使用起来很简单:

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

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

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

using UnityEngine;

using UnityEditor;

using System.IO;

public class CreateFont : EditorWindow

{

[MenuItem("Tools/创建字体(sprite)")]

public static void Open()

{

GetWindow("创建字体");

}

private Texture2D tex;

private string fontName;

private string fontPath;

private void OnGUI()

{

GUILayout.BeginVertical();

GUILayout.BeginHorizontal();

GUILayout.Label("字体图片:");

tex = (Texture2D)EditorGUILayout.ObjectField(tex, typeof(Texture2D), true);

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

GUILayout.Label("字体名称:");

fontName = EditorGUILayout.TextField(fontName);

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

if (GUILayout.Button(string.IsNullOrEmpty(fontPath) ? "选择路径" : fontPath))

{

fontPath = EditorUtility.OpenFolderPanel("字体路径", Application.dataPath, "");

if (string.IsNullOrEmpty(fontPath))

{

Debug.Log("取消选择路径");

}

else

{

fontPath = fontPath.Replace(Application.dataPath, "") + "/";

}

}

GUILayout.EndHorizontal();

GUILayout.BeginHorizontal();

if (GUILayout.Button("创建"))

{

Create();

}

GUILayout.EndHorizontal();

GUILayout.EndVertical();

}

private void Create()

{

if (tex == null)

{

Debug.LogWarning("创建失败,图片为空!");

return;

}

if (string.IsNullOrEmpty(fontPath))

{

Debug.LogWarning("字体路径为空!");

return;

}

if (fontName == null)

{

Debug.LogWarning("创建失败,字体名称为空!");

return;

}

else

{

if (File.Exists(Application.dataPath + fontPath + fontName + ".fontsettings"))

{

Debug.LogError("创建失败,已存在同名字体文件");

return;

}

if (File.Exists(Application.dataPath + fontPath + fontName + ".mat"))

{

Debug.LogError("创建失败,已存在同名字体材质文件");

return;

}

}

string selectionPath = AssetDatabase.GetAssetPath(tex);

if (selectionPath.Contains("/Resources/"))

{

string selectionExt = Path.GetExtension(selectionPath);

if (selectionExt.Length == 0)

{

Debug.LogError("创建失败!");

return;

}

string fontPathName = fontPath + fontName + ".fontsettings";

string matPathName = fontPath + fontName + ".mat";

float lineSpace = 0.1f;

//string loadPath = selectionPath.Remove(selectionPath.Length - selectionExt.Length).Replace("Assets/Resources/", "");

string loadPath = selectionPath.Replace(selectionExt, "").Substring(selectionPath.IndexOf("/Resources/") + "/Resources/".Length);

Sprite[] sprites = Resources.LoadAll(loadPath);

if (sprites.Length > 0)

{

Material mat = new Material(Shader.Find("GUI/Text Shader"));

mat.SetTexture("_MainTex", tex);

Font m_myFont = new Font();

m_myFont.material = mat;

CharacterInfo[] characterInfo = new CharacterInfo[sprites.Length];

for (int i = 0; i < sprites.Length; i++)

{

if (sprites[i].rect.height > lineSpace)

{

lineSpace = sprites[i].rect.height;

}

}

for (int i = 0; i < sprites.Length; i++)

{

Sprite spr = sprites[i];

CharacterInfo info = new CharacterInfo();

try

{

info.index = System.Convert.ToInt32(spr.name);

}

catch

{

Debug.LogError("创建失败,Sprite名称错误!");

return;

}

Rect rect = spr.rect;

float pivot = spr.pivot.y / rect.height - 0.5f;

if (pivot > 0)

{

pivot = -lineSpace / 2 - spr.pivot.y;

}

else if (pivot < 0)

{

pivot = -lineSpace / 2 + rect.height - spr.pivot.y;

}

else

{

pivot = -lineSpace / 2;

}

int offsetY = (int)(pivot + (lineSpace - rect.height) / 2);

info.uvBottomLeft = new Vector2((float)rect.x / tex.width, (float)(rect.y) / tex.height);

info.uvBottomRight = new Vector2((float)(rect.x + rect.width) / tex.width, (float)(rect.y) / tex.height);

info.uvTopLeft = new Vector2((float)rect.x / tex.width, (float)(rect.y + rect.height) / tex.height);

info.uvTopRight = new Vector2((float)(rect.x + rect.width) / tex.width, (float)(rect.y + rect.height) / tex.height);

info.minX = 0;

info.minY = -(int)rect.height - offsetY;

info.maxX = (int)rect.width;

info.maxY = -offsetY;

info.advance = (int)rect.width;

characterInfo[i] = info;

}

AssetDatabase.CreateAsset(mat, "Assets" + matPathName);

AssetDatabase.CreateAsset(m_myFont, "Assets" + fontPathName);

m_myFont.characterInfo = characterInfo;

EditorUtility.SetDirty(m_myFont);

AssetDatabase.SaveAssets();

AssetDatabase.Refresh();//刷新资源

Debug.Log("创建字体成功");

}

else

{

Debug.LogError("图集错误!");

}

}

else

{

Debug.LogError("创建失败,选择的图片不在Resources文件夹内!");

}

}

}

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

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

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

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

unity 字体width_Unity制作自定义字体的两种方法相关推荐

  1. unity 2D游戏开发 制作帧动画的两种方法

    本小主在这里给大家分享一下unity 2D游戏开发中制作帧动画的两种方法. 比较简单,一学即会. 方法一: 是用代码控制: 先创建一个2D工程.导入图片资源,并设置texture的texture ty ...

  2. Unity中ugui如何制作不规则按键的两种方法

    Unity中ugui如何制作不规则按键的两种方法 两种不同的方案 目前,关于这个问题如何,解决通过搜索引擎我们能找到两种不同的方案: 多边形碰撞器: 该方法是指给精灵(Sprite)添加一个多边形碰撞 ...

  3. BIGEMAP使用Unity3D制作真实地形的两种方法

    使用Unity3D制作真实地形的两种方法: 1.在SceneView中使用height tools直接绘制: 2.使用外部工具制作的heightmaps: 具体操作如下: 1.准备一块DEM数据,格式 ...

  4. 使用Unity3D制作真实地形的两种方法

    使用Unity3D制作真实地形的两种方法: 1.在SceneView中使用height tools直接绘制: 2.使用外部工具制作的heightmaps: 具体操作如下: 1.准备一块DEM数据,格式 ...

  5. 使用vivado调用自定义IP的两种方法

    使用vivado调用自定义IP的两种方法 方法一:采用Creat Block Design以图形化界面方式,即原理图方式调用自定义IP,例如下图: 方法二:采用代码方式,即类似函数方式调用自定义IP, ...

  6. Hexo自定义页面的两种方法

    原文地址:http://refined-x.com/2017/07/10/Hexo自定义页面的方法/. Hexo是静态页博客生成利器,同很多博主一样,前端路上原创技术博客也是使用Hexo生成并托管在G ...

  7. Unity 得到游戏组件的常用的两种方法

    看了一些别人总结的得到组件的方法,很详细,但是初学者会看得眼花缭乱(我就是),这里给出最常用的得到游戏组件的两种方法.这里都以MeshRenderer 组件为例 1.当脚本挂在当前游戏物体,需要得到当 ...

  8. unity 使用BMFont 制作自定义字体

    unity cocos 使用BMFont,生成自定义字体 1.BMFont下载地址 http://www.angelcode.com/products/bmfont/ 本篇讲解如何利用美工提供的字符图 ...

  9. Tableau制作漏斗图的两种方法

    很多业务都是由多个流程.多个环节组成,每一个环节的数据很多时候呈现逐级递减的趋势,如电商中用户的下单数据. 漏斗图主要用于展现每个环节的留存.转化情况,本文讲一下如何使用Tableau制作漏斗图. 本 ...

  10. Unity 使用BitmapFont制作自定义字体

    BitmapFont下载地址BMFont - AngelCode.com (通过NGUI制作) 选择一个文件夹创建txt文本,在文本输入自定义内容或者在百度搜常用中文千字(按自己要求) 这里的冒号是中 ...

最新文章

  1. 2020人工神经网络第一次作业-参考答案第五部分
  2. NSStirng、NSArray、以及枚举(Method小集合)
  3. js算法入门(2)--哈希表
  4. boost::lambda::bind用法的测试程序
  5. “平行驾驶”是无人车上路的安全高效智能途径!
  6. pandas filter_数据分析之Pandas操作(2)
  7. 专访20年技术老兵云郎:16年峰回路,每一步都是更好的沉淀
  8. Django应用部署 - 上线指南
  9. linux win10五笔码表,wubiLex(Win10微软五笔码表安装管理助手)V9.6.0.1 正式版
  10. 为什么成功启动ngnix之后还是无法用ip地址访问网站
  11. 在线文档转换接口 word,excel,ppt等在线文件转pdf、png
  12. 串口通信校验方式(even,odd,space,mark)UART数据波形分析
  13. 程序员涨工资大多数靠跳槽吗?
  14. 大年初一,给大家发红包了!
  15. 题解 luoguP2928 【[USACO09HOL]牛的打手Cattle Bruisers】
  16. 使用BMFont工具来精简字体库
  17. Xavier NX+KU040图像处理卡
  18. iOS —— SQLite3 功能使用详解 (三)
  19. 物权法全文内容有哪些呢-广告外链_有哪些网络推广免费平台?通过哪些平台进行免费推广及注意事项?...
  20. 3、关于onclick事件的两种写法

热门文章

  1. 我为什么放弃用了近10年的金山
  2. 软件各项会议评审意见模版
  3. E-R图练习(邮件客户端系统)
  4. gps信号用什么软件测试,gps信号检测软件
  5. Tapestry5项目的导入
  6. eclipse安装教程 以及汉化包安装
  7. 21天学通JAVA-第7版 入门到精通完美高清PDFamp;光盘源代码下载
  8. linux安装vmd软件步骤,VMD软件使用指南.PDF
  9. esp ghost引导_UEFI引导修复教程和工具
  10. ContextCapture(smart3D)10.20.下载安装教程