一脯倚、字體制作工具下載
下載安裝windows下免費的位圖字體制作工具Bitmap Font Generator
下載地址 http://www.angelcode.com/products/bmfont/
二、創(chuàng)建字體文件
清空字符
Edit->Clear all chars in font
載入新字符
Edit->Selecting text from file...
要提前準(zhǔn)備字體文件围肥,需注意txt文件的格式要對應(yīng),如果使用的Unicode編碼就Font設(shè)置的時候就要選中Unicode著淆。
打開軟件->Options -> Font Settings
Size可以設(shè)定字體大小末荐,需要多大就設(shè)定多大,這里是12
Height可以設(shè)定字體的拉伸高度嘉栓,保持默認(rèn)100%就可以了
打開軟件->Options -> Export Options
Padding:文字的內(nèi)邊框,或者理解為文字的周邊留空要多大 做后期樣式時這個屬性很重要拓诸,需要預(yù)留空間來給描邊侵佃、發(fā)光等特效使用 比如我預(yù)計我的樣式要加一個2px的邊框,然后加一個右下角2px的投影效果奠支,所以我設(shè)定了padding:2px 4px 4px 2px
BitDepth:必須32位馋辈,否則沒有透明層
Presets:字體初始化的預(yù)設(shè)的顏色通道設(shè)定,也就是說字體的初始顏色設(shè)定是什么樣的倍谜,建議都用白色字首有,可以直接設(shè)定為White text with alpha,即白色字透明底枢劝。
Font descript:字體描述文件,可以使用text或者xml 也就是fnt文件格式
Textures:紋理圖片格式卜壕,一般選png您旁。
最后設(shè)置好之后保存,會生成一個.png和一個.fnt兩個文件
option->Save bitmap font as
三轴捎、Unity中生成字體
生成的png和fnt文件導(dǎo)入Untiy中鹤盒,創(chuàng)建一個工具:
namespace src.test
{
using UnityEngine;
using UnityEditor;
using System.IO;
using System.Xml;
using System;
public class BitmapFontExporter : ScriptableWizard
{
[MenuItem("BitmapFontExporter/Create")]
private static void CreateFont()
{
ScriptableWizard.DisplayWizard<BitmapFontExporter>("Create Font");
}
public TextAsset fontFile;
public Texture2D textureFile;
private void OnWizardCreate()
{
if (fontFile == null || textureFile == null)
{
return;
}
string path = EditorUtility.SaveFilePanelInProject("Save Font", fontFile.name, "", "");
if (!string.IsNullOrEmpty(path))
{
ResolveFont(path);
}
}
private void ResolveFont(string exportPath)
{
if (!fontFile) throw new UnityException(fontFile.name + "is not a valid font-xml file");
Font font = new Font();
XmlDocument xml = new XmlDocument();
xml.LoadXml(fontFile.text);
XmlNode info = xml.GetElementsByTagName("info")[0];
XmlNodeList chars = xml.GetElementsByTagName("chars")[0].ChildNodes;
CharacterInfo[] charInfos = new CharacterInfo[chars.Count];
for (int cnt = 0; cnt < chars.Count; cnt++)
{
XmlNode node = chars[cnt];
CharacterInfo charInfo = new CharacterInfo();
charInfo.index = ToInt(node, "id");
charInfo.width = ToInt(node, "xadvance");
charInfo.uv = GetUV(node);
charInfo.vert = GetVert(node);
charInfos[cnt] = charInfo;
}
Shader shader = Shader.Find("Unlit/Transparent");
Material material = new Material(shader);
material.mainTexture = textureFile;
AssetDatabase.CreateAsset(material, exportPath + ".mat");
font.material = material;
font.name = info.Attributes.GetNamedItem("face").InnerText;
font.characterInfo = charInfos;
AssetDatabase.CreateAsset(font, exportPath + ".fontsettings");
}
private Rect GetUV(XmlNode node)
{
Rect uv = new Rect();
uv.x = ToFloat(node, "x") / textureFile.width;
uv.y = ToFloat(node, "y") / textureFile.height;
uv.width = ToFloat(node, "width") / textureFile.width;
uv.height = ToFloat(node, "height") / textureFile.height;
uv.y = 1f - uv.y - uv.height;
return uv;
}
private Rect GetVert(XmlNode node)
{
Rect uv = new Rect();
uv.x = ToFloat(node, "xoffset");
uv.y = ToFloat(node, "yoffset");
uv.width = ToFloat(node, "width");
uv.height = ToFloat(node, "height");
uv.y = -uv.y;
uv.height = -uv.height;
return uv;
}
private int ToInt(XmlNode node, string name)
{
return Convert.ToInt32(node.Attributes.GetNamedItem(name).InnerText);
}
private float ToFloat(XmlNode node, string name)
{
return (float) ToInt(node, name);
}
}
}
工具生成后會在Unity窗口目錄多出一個入口 BitmapFontExporter->Create
引入fnt和png兩個文件
點擊create后就會生成字體文件了,就可以使用到Text中了
四侦副、文字描邊腳本
創(chuàng)建一個Shader:
Shader "Custom/OutlineShader"
{
Properties
{
_MainTex ("Main Texture", 2D) = "white" {}
_Color ("Tint", Color) = (1, 1, 1, 1)
_OutlineColor ("Outline Color", Color) = (1, 1, 1, 1)
_OutlineWidth ("Outline Width", Int) = 1
_StencilComp ("Stencil Comparison", Float) = 8
_Stencil ("Stencil ID", Float) = 0
_StencilOp ("Stencil Operation", Float) = 0
_StencilWriteMask ("Stencil Write Mask", Float) = 255
_StencilReadMask ("Stencil Read Mask", Float) = 255
_ColorMask ("Color Mask", Float) = 15
[Toggle(UNITY_UI_ALPHACLIP)] _UseUIAlphaClip ("Use Alpha Clip", Float) = 0
}
SubShader
{
Tags
{
"Queue"="Transparent"
"IgnoreProjector"="True"
"RenderType"="Transparent"
"PreviewType"="Plane"
"CanUseSpriteAtlas"="True"
}
Stencil
{
Ref [_Stencil]
Comp [_StencilComp]
Pass [_StencilOp]
ReadMask [_StencilReadMask]
WriteMask [_StencilWriteMask]
}
Cull Off
Lighting Off
ZWrite Off
ZTest [unity_GUIZTestMode]
Blend SrcAlpha OneMinusSrcAlpha
ColorMask [_ColorMask]
Pass
{
Name "OUTLINE"
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
sampler2D _MainTex;
fixed4 _Color;
fixed4 _TextureSampleAdd;
float4 _MainTex_TexelSize;
float4 _OutlineColor;
int _OutlineWidth;
struct appdata
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
float2 texcoord1 : TEXCOORD1;
float2 texcoord2 : TEXCOORD2;
fixed4 color : COLOR;
};
struct v2f
{
float4 vertex : SV_POSITION;
float2 texcoord : TEXCOORD0;
float2 uvOriginXY : TEXCOORD1;
float2 uvOriginZW : TEXCOORD2;
fixed4 color : COLOR;
};
v2f vert(appdata IN)
{
v2f o;
o.vertex = UnityObjectToClipPos(IN.vertex);
o.texcoord = IN.texcoord;
o.uvOriginXY = IN.texcoord1;
o.uvOriginZW = IN.texcoord2;
o.color = IN.color * _Color;
return o;
}
fixed IsInRect(float2 pPos, float2 pClipRectXY, float2 pClipRectZW)
{
pPos = step(pClipRectXY, pPos) * step(pPos, pClipRectZW);
return pPos.x * pPos.y;
}
fixed SampleAlpha(int pIndex, v2f IN)
{
const fixed sinArray[12] = { 0, 0.5, 0.866, 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5 };
const fixed cosArray[12] = { 1, 0.866, 0.5, 0, -0.5, -0.866, -1, -0.866, -0.5, 0, 0.5, 0.866 };
float2 pos = IN.texcoord + _MainTex_TexelSize.xy * float2(cosArray[pIndex], sinArray[pIndex]) * _OutlineWidth;
return IsInRect(pos, IN.uvOriginXY, IN.uvOriginZW) * (tex2D(_MainTex, pos) + _TextureSampleAdd).w * _OutlineColor.w;
}
fixed4 frag(v2f IN) : SV_Target
{
fixed4 color = (tex2D(_MainTex, IN.texcoord) + _TextureSampleAdd) * IN.color;
if (_OutlineWidth > 0)
{
color.w *= IsInRect(IN.texcoord, IN.uvOriginXY, IN.uvOriginZW);
half4 val = half4(_OutlineColor.x, _OutlineColor.y, _OutlineColor.z, 0);
val.w += SampleAlpha(0, IN);
val.w += SampleAlpha(1, IN);
val.w += SampleAlpha(2, IN);
val.w += SampleAlpha(3, IN);
val.w += SampleAlpha(4, IN);
val.w += SampleAlpha(5, IN);
val.w += SampleAlpha(6, IN);
val.w += SampleAlpha(7, IN);
val.w += SampleAlpha(8, IN);
val.w += SampleAlpha(9, IN);
val.w += SampleAlpha(10, IN);
val.w += SampleAlpha(11, IN);
val.w = clamp(val.w, 0, 1);
color = (val * (1.0 - color.a)) + (color * color.a);
}
return color;
}
ENDCG
}
}
}
創(chuàng)建一個材質(zhì)將Shader掛載在材質(zhì)球上
創(chuàng)建一個OutlineEx腳本
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace src.Tool
{
/// <summary>
/// UGUI描邊
/// </summary>
public class OutlineEx : BaseMeshEffect
{
public Color OutlineColor = Color.white;
[Range(0, 6)] public int OutlineWidth = 0;
private static List<UIVertex> m_VetexList = new List<UIVertex>();
protected override void Start()
{
base.Start();
var shader = Shader.Find("Custom/OutlineShader");
base.graphic.material = new Material(shader);
var v1 = base.graphic.canvas.additionalShaderChannels;
var v2 = AdditionalCanvasShaderChannels.TexCoord1;
if ((v1 & v2) != v2)
{
base.graphic.canvas.additionalShaderChannels |= v2;
}
v2 = AdditionalCanvasShaderChannels.TexCoord2;
if ((v1 & v2) != v2)
{
base.graphic.canvas.additionalShaderChannels |= v2;
}
this._Refresh();
}
#if UNITY_EDITOR
protected override void OnValidate()
{
base.OnValidate();
if (base.graphic.material != null)
{
this._Refresh();
}
}
#endif
private void _Refresh()
{
base.graphic.material.SetColor("_OutlineColor", this.OutlineColor);
base.graphic.material.SetInt("_OutlineWidth", this.OutlineWidth);
base.graphic.SetVerticesDirty();
}
public override void ModifyMesh(VertexHelper vh)
{
vh.GetUIVertexStream(m_VetexList);
this._ProcessVertices();
vh.Clear();
vh.AddUIVertexTriangleStream(m_VetexList);
}
private void _ProcessVertices()
{
for (int i = 0, count = m_VetexList.Count - 3; i <= count; i += 3)
{
var v1 = m_VetexList[i];
var v2 = m_VetexList[i + 1];
var v3 = m_VetexList[i + 2];
// 計算原頂點坐標(biāo)中心點
//
var minX = _Min(v1.position.x, v2.position.x, v3.position.x);
var minY = _Min(v1.position.y, v2.position.y, v3.position.y);
var maxX = _Max(v1.position.x, v2.position.x, v3.position.x);
var maxY = _Max(v1.position.y, v2.position.y, v3.position.y);
var posCenter = new Vector2(minX + maxX, minY + maxY) * 0.5f;
// 計算原始頂點坐標(biāo)和UV的方向
//
Vector2 triX, triY, uvX, uvY;
Vector2 pos1 = v1.position;
Vector2 pos2 = v2.position;
Vector2 pos3 = v3.position;
if (Mathf.Abs(Vector2.Dot((pos2 - pos1).normalized, Vector2.right))
> Mathf.Abs(Vector2.Dot((pos3 - pos2).normalized, Vector2.right)))
{
triX = pos2 - pos1;
triY = pos3 - pos2;
uvX = v2.uv0 - v1.uv0;
uvY = v3.uv0 - v2.uv0;
}
else
{
triX = pos3 - pos2;
triY = pos2 - pos1;
uvX = v3.uv0 - v2.uv0;
uvY = v2.uv0 - v1.uv0;
}
// 計算原始UV框
//
var uvMin = _Min(v1.uv0, v2.uv0, v3.uv0);
var uvMax = _Max(v1.uv0, v2.uv0, v3.uv0);
var uvOrigin = new Vector4(uvMin.x, uvMin.y, uvMax.x, uvMax.y);
// 為每個頂點設(shè)置新的Position和UV侦锯,并傳入原始UV框
//
v1 = _SetNewPosAndUV(v1, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
v2 = _SetNewPosAndUV(v2, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
v3 = _SetNewPosAndUV(v3, this.OutlineWidth, posCenter, triX, triY, uvX, uvY, uvOrigin);
// 應(yīng)用設(shè)置后的UIVertex
//
m_VetexList[i] = v1;
m_VetexList[i + 1] = v2;
m_VetexList[i + 2] = v3;
}
}
private static UIVertex _SetNewPosAndUV(UIVertex pVertex, int pOutLineWidth,
Vector2 pPosCenter,
Vector2 pTriangleX, Vector2 pTriangleY,
Vector2 pUVX, Vector2 pUVY,
Vector4 pUVOrigin)
{
// Position
var pos = pVertex.position;
var posXOffset = pos.x > pPosCenter.x ? pOutLineWidth : -pOutLineWidth;
var posYOffset = pos.y > pPosCenter.y ? pOutLineWidth : -pOutLineWidth;
pos.x += posXOffset;
pos.y += posYOffset;
pVertex.position = pos;
// UV
var uv = pVertex.uv0;
uv += (Vector4) pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1);
uv += (Vector4) pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1);
pVertex.uv0 = uv;
// 原始UV框
pVertex.uv1 = new Vector2(pUVOrigin.x, pUVOrigin.y);
pVertex.uv2 = new Vector2(pUVOrigin.z, pUVOrigin.w);
return pVertex;
}
private static float _Min(float pA, float pB, float pC)
{
return Mathf.Min(Mathf.Min(pA, pB), pC);
}
private static float _Max(float pA, float pB, float pC)
{
return Mathf.Max(Mathf.Max(pA, pB), pC);
}
private static Vector2 _Min(Vector2 pA, Vector2 pB, Vector2 pC)
{
return new Vector2(_Min(pA.x, pB.x, pC.x), _Min(pA.y, pB.y, pC.y));
}
private static Vector2 _Max(Vector2 pA, Vector2 pB, Vector2 pC)
{
return new Vector2(_Max(pA.x, pB.x, pC.x), _Max(pA.y, pB.y, pC.y));
}
}
}
五、使用
創(chuàng)建一個Text掛載制作好的字體和腳本