您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關利用Unity怎么制作一個自定義字體,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
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<CreateFontFromFnt>("創建字體");
}
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<CharacterInfo>();
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<CreateFont>("創建字體");
}
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<Sprite>(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怎么制作一個自定義字體就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。