在项目开发中,和ui界面打交道是必不可少的,但是最近发现ugui提供的text组件,在很多情况下不能满足美术的需求,这就对text组件进行扩展编辑,使其尽可能满足项目的需求,这里主要实现字间距、字体颜色、字体阴影、字体描边、字体根据需求自动选择字体和颜色字号等功能。
一、关于字间距的实现:
public void PopulateMesh(VertexHelper toFill)
{
if (UseTextSpacing)
{
if (toFill.currentVertCount == 0)
{
return;
}
List<UIVertex> vertexs = new List<UIVertex>();
toFill.GetUIVertexStream(vertexs);
int indexCount = toFill.currentIndexCount;
UIVertex vt;
for (int i = 6; i < indexCount; i++)
{
//第一个字不用改变位置
vt = vertexs[i];
vt.position += new Vector3(m_TextSpacing * (i / 6), 0, 0);
vertexs[i] = vt;
//以下注意点与索引的对应关系
if (i % 6 <= 2)
{
toFill.SetUIVertex(vt, (i / 6) * 4 + i % 6);
}
if (i % 6 == 4)
{
toFill.SetUIVertex(vt, (i / 6) * 4 + i % 6 - 1);
}
}
}
}
二、关于字体颜色的调整:
public void PopulateMesh(VertexHelper toFill, RectTransform rectTransform, Color color)
{
if (UseVertexColor)
{
Vector2 min = rectTransform.pivot;
min.Scale(-rectTransform.rect.size);
Vector2 max = rectTransform.rect.size + min;
int len = toFill.currentVertCount;
for (int i = 0; i < len; i++)
{
UIVertex v = new UIVertex();
toFill.PopulateUIVertex(ref v, i);
v.color = RemapColor(min, max, color, v.position);
toFill.SetUIVertex(v, i);
}
}
}
private Color RemapColor(Vector2 min, Vector2 max, Color color, Vector2 pos)
{
float x01 = max.x == min.x ? 0f : Mathf.Clamp01((pos.x - min.x) / (max.x - min.x));
float y01 = max.y == min.y ? 0f : Mathf.Clamp01((pos.y - min.y) / (max.y - min.y));
x01 -= VertexColorOffset.x * (VertexColorOffset.x > 0f ? x01 : (1f - x01));
y01 -= VertexColorOffset.y * (VertexColorOffset.y > 0f ? y01 : (1f - y01));
Color newColor = Color.Lerp(
Color.Lerp(VertexBottomLeft, VertexBottomRight, x01),
Color.Lerp(VertexTopLeft, VertexTopRight, x01),
y01
);
switch (VertexColorFilter)
{
default:
case ColorFilterType.Additive:
return color + newColor;
case ColorFilterType.OverLap:
float a = Mathf.Max(newColor.a, color.a);
newColor = Color.Lerp(color, newColor, newColor.a);
newColor.a = a;
return newColor;
}
}
实现效果:
三、实现字体阴影:
public Vector2 effectDistance
{
get { return m_EffectDistance; }
set
{
if (value.x > kMaxEffectDistance)
value.x = kMaxEffectDistance;
if (value.x < -kMaxEffectDistance)
value.x = -kMaxEffectDistance;
if (value.y > kMaxEffectDistance)
value.y = kMaxEffectDistance;
if (value.y < -kMaxEffectDistance)
value.y = -kMaxEffectDistance;
if (m_EffectDistance == value)
return;
m_EffectDistance = value;
}
}
protected void ApplyShadow(List<UIVertex> verts, Vector2 min, Vector2 max, Color32 color, float x, float y)
{
UIVertex vt;
int start = 0, end = verts.Count;
var neededCapacity = verts.Count + end - start;
if (verts.Capacity < neededCapacity)
verts.Capacity = neededCapacity;
for (int i = start; i < end; ++i)
{
vt = verts[i];
verts.Add(vt);
Vector3 v = vt.position;
v.x += x;
v.y += y;
vt.position = v;
vt.color = RemapColor(min, max, color, v);
verts[i] = vt;
}
}
private Color RemapColor(Vector2 min, Vector2 max, Color color, Vector2 pos)
{
float x01 = max.x == min.x ? 0f : Mathf.Clamp01((pos.x - min.x) / (max.x - min.x));
float y01 = max.y == min.y ? 0f : Mathf.Clamp01((pos.y - min.y) / (max.y - min.y));
x01 -= m_VertexColorOffset.x * (m_VertexColorOffset.x > 0f ? x01 : (1f - x01));
y01 -= m_VertexColorOffset.y * (m_VertexColorOffset.y > 0f ? y01 : (1f - y01));
Color newColor = Color.Lerp(
Color.Lerp(VertexBottomLeft, VertexBottomRight, x01),
Color.Lerp(VertexTopLeft, VertexTopRight, x01),
y01
);
//使用全新颜色 不继承原有的
return newColor;
//return color * newColor;
}
public void PopulateMesh(VertexHelper vh, RectTransform rectTransform, Color color)
{
if (UseShadow)
{
Vector2 min = rectTransform.pivot;
min.Scale(-rectTransform.rect.size);
Vector2 max = rectTransform.rect.size + min;
List<UIVertex> output = new List<UIVertex>();
vh.GetUIVertexStream(output);
ApplyShadow(output, min, max, color, effectDistance.x, effectDistance.y);
vh.Clear();
vh.AddUIVertexTriangleStream(output);
}
}
四、字体描边的实现
public Vector2 EffectDistance
{
get { return m_EffectDistance; }
set
{
if (value.x > kMaxEffectDistance)
value.x = kMaxEffectDistance;
if (value.x < -kMaxEffectDistance)
value.x = -kMaxEffectDistance;
if (value.y > kMaxEffectDistance)
value.y = kMaxEffectDistance;
if (value.y < -kMaxEffectDistance)
value.y = -kMaxEffectDistance;
if (m_EffectDistance == value)
return;
m_EffectDistance = value;
}
}
protected void ApplyShadowZeroAlloc(List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
{
UIVertex vt;
var neededCapacity = verts.Count + end - start;
if (verts.Capacity < neededCapacity)
verts.Capacity = neededCapacity;
for (int i = start; i < end; ++i)
{
vt = verts[i];
verts.Add(vt);
Vector3 v = vt.position;
v.x += x;
v.y += y;
vt.position = v;
var newColor = color;
newColor.a = (byte)((newColor.a * verts[i].color.a) / 255);
vt.color = newColor;
verts[i] = vt;
}
}
public void PopulateMesh(VertexHelper vh)
{
if (UseOutline)
{
List<UIVertex> verts = new List<UIVertex>();
vh.GetUIVertexStream(verts);
var neededCpacity = verts.Count * 5;
if (verts.Capacity < neededCpacity)
verts.Capacity = neededCpacity;
var start = 0;
var end = verts.Count;
ApplyShadowZeroAlloc(verts, EffectColor, start, verts.Count, EffectDistance.x, EffectDistance.y);
start = end;
end = verts.Count;
ApplyShadowZeroAlloc(verts, EffectColor, start, verts.Count, EffectDistance.x, -EffectDistance.y);
start = end;
end = verts.Count;
ApplyShadowZeroAlloc(verts, EffectColor, start, verts.Count, -EffectDistance.x, EffectDistance.y);
start = end;
end = verts.Count;
ApplyShadowZeroAlloc(verts, EffectColor, start, verts.Count, -EffectDistance.x, -EffectDistance.y);
vh.Clear();
vh.AddUIVertexTriangleStream(verts);
}
}
五、本地默认颜色和字体的修改,主要实现直接调整为指定文字格式和颜色的功能,例如游戏中有tip、标题、二级弹框等字体,可以直接设置为指定的格式
public void UpdateText(Text target)
{
txt_Target = target;
if (m_UseLocalization == false)
return;
m_TextStyle = (TextStyle)(int)m_TextStyleCN;
//通过类型选择字体和颜色
var color_ = Color.black;
TextStyleFont fontStyle = new TextStyleFont()
{
font = Resources.Load<Font>(""),
fontSize = 20
};
switch (m_TextStyle)
{
case TextStyle.SystemTitle:
color_ = Color.blue;
txt_Target.font = Resources.Load<Font>("Font/FZZDHJW");
txt_Target.fontSize = 35;
break;
case TextStyle.SecondTipTitle:
color_ = Color.yellow;
txt_Target.font = Resources.Load<Font>("Font/FZZDHJW");
txt_Target.fontSize = 25;
break;
case TextStyle.SmallTipTitle:
color_ = Color.yellow;
txt_Target.font = Resources.Load<Font>("Font/MSYHTTF");
txt_Target.fontSize = 25;
break;
}
txt_Target.color = color_;
}
六、将上述功能添加到text组件中:
这里会定义上述的五个处理器,然后继承OnPopulateMesh,实现上面的功能:
protected override void OnPopulateMesh(VertexHelper toFill)
{
base.OnPopulateMesh(toFill);
m_FontSpacingHandler.PopulateMesh(toFill);
m_VertexColorHandler.PopulateMesh(toFill, rectTransform, color);
m_TextShadowHandler.PopulateMesh(toFill, rectTransform, color);
m_TextOutlineHandler.PopulateMesh(toFill);
}
重新绘制text组件下的信息:
[MenuItem("GameObject/UI/UGUI Plus/Text Plus")]
public static void CreateTextPlus()
{
GameObject root = new GameObject("Text", typeof(RectTransform), typeof(TextPlus));
ResetInCanvasFor((RectTransform)root.transform);
root.GetComponent<TextPlus>().text = "Text Plus";
var text = root.GetComponent<TextPlus>();
text.text = "Text Plus";
text.color = Color.black;
text.alignment = TextAnchor.MiddleCenter;
root.transform.localPosition = Vector3.zero;
}
public static void TextSpacingGUI(SerializedProperty m_UseTextSpacing, SerializedProperty m_TextSpacing, ref bool m_TextSpacingPanelOpen)
{
LayoutF(() =>
{
EditorGUILayout.PropertyField(m_UseTextSpacing);
if (m_UseTextSpacing.boolValue)
{
Space();
LayoutH(() => {
EditorGUI.PropertyField(GUIRect(0, 18), m_TextSpacing, new GUIContent());
});
}
}, "Text Spacing", ref m_TextSpacingPanelOpen, true);
}
public static void VertexColorGUI(SerializedProperty m_UseVertexColor, SerializedProperty m_VertexTopLeft, SerializedProperty m_VertexTopRight, SerializedProperty m_VertexBottomLeft, SerializedProperty m_VertexBottomRight, SerializedProperty m_VertexColorFilter, SerializedProperty m_VertexColorOffset, ref bool m_VertexColorPanelOpen)
{
LayoutF(() => {
EditorGUILayout.PropertyField(m_UseVertexColor);
if (m_UseVertexColor.boolValue)
{
Space();
LayoutH(() => {
EditorGUI.PropertyField(GUIRect(0, 18), m_VertexTopLeft, new GUIContent());
Space();
EditorGUI.PropertyField(GUIRect(0, 18), m_VertexTopRight, new GUIContent());
});
Space();
LayoutH(() => {
EditorGUI.PropertyField(GUIRect(0, 18), m_VertexBottomLeft, new GUIContent());
Space();
EditorGUI.PropertyField(GUIRect(0, 18), m_VertexBottomRight, new GUIContent());
});
Space();
m_VertexColorFilter.enumValueIndex = (int)(VertexColorHandler.ColorFilterType)EditorGUILayout.EnumPopup(
new GUIContent("Filter"), (VertexColorHandler.ColorFilterType)m_VertexColorFilter.enumValueIndex
);
Vector2 newOffset = EditorGUILayout.Vector2Field("Offset", m_VertexColorOffset.vector2Value);
newOffset.x = Mathf.Clamp(newOffset.x, -1f, 1f);
newOffset.y = Mathf.Clamp(newOffset.y, -1f, 1f);
m_VertexColorOffset.vector2Value = newOffset;
Space();
}
}, "Vertex Color", ref m_VertexColorPanelOpen, true);
}
public static void TextShadowGUI(SerializedProperty m_UseShadow, SerializedProperty m_ShadowColorTopLeft, SerializedProperty m_ShadowColorTopRight,
SerializedProperty m_ShadowColorBottomLeft, SerializedProperty m_ShadowColorBottomRight, SerializedProperty m_ShadowEffectDistance, ref bool m_TextShadowPanelOpen)
{
LayoutF(() =>
{
EditorGUILayout.PropertyField(m_UseShadow);
if (m_UseShadow.boolValue)
{
Space();
LayoutH(() => {
EditorGUI.PropertyField(GUIRect(0, 18), m_ShadowColorTopLeft, new GUIContent());
Space();
EditorGUI.PropertyField(GUIRect(0, 18), m_ShadowColorTopRight, new GUIContent());
});
Space();
LayoutH(() => {
EditorGUI.PropertyField(GUIRect(0, 18), m_ShadowColorBottomLeft, new GUIContent());
Space();
EditorGUI.PropertyField(GUIRect(0, 18), m_ShadowColorBottomRight, new GUIContent());
});
Space();
EditorGUILayout.PropertyField(m_ShadowEffectDistance);
}
}, "Shadow", ref m_TextShadowPanelOpen, true);
}
public static void SimpleUseGUI(string title, ref bool m_PanelOpen, float space, SerializedProperty useThis, params SerializedProperty[] sps)
{
LayoutF(() => {
EditorGUILayout.PropertyField(useThis);
if (useThis.boolValue)
{
foreach (var s in sps)
{
if (s != null)
{
EditorGUILayout.PropertyField(s);
}
}
}
}, title, ref m_PanelOpen, true);
}
private static void ResetInCanvasFor(RectTransform root)
{
root.SetParent(Selection.activeTransform);
if (!InCanvas(root))
{
Transform canvasTF = GetCreateCanvas();
root.SetParent(canvasTF);
}
if (!Transform.FindObjectOfType<UnityEngine.EventSystems.EventSystem>())
{
GameObject eg = new GameObject("EventSystem");
eg.AddComponent<UnityEngine.EventSystems.EventSystem>();
eg.AddComponent<UnityEngine.EventSystems.StandaloneInputModule>();
}
root.localScale = Vector3.one;
root.localPosition = new Vector3(root.localPosition.x, root.localPosition.y, 0f);
Selection.activeGameObject = root.gameObject;
}
private static bool InCanvas(Transform tf)
{
while (tf.parent)
{
tf = tf.parent;
if (tf.GetComponent<Canvas>())
{
return true;
}
}
return false;
}
private static Transform GetCreateCanvas()
{
Canvas c = Object.FindObjectOfType<Canvas>();
if (c)
{
return c.transform;
}
else
{
GameObject g = new GameObject("Canvas");
c = g.AddComponent<Canvas>();
c.renderMode = RenderMode.ScreenSpaceOverlay;
g.AddComponent<CanvasScaler>();
g.AddComponent<GraphicRaycaster>();
return g.transform;
}
}
private static void LayoutF(System.Action action, string label, ref bool open, bool box = false)
{
bool _open = open;
LayoutV(() => {
_open = GUILayout.Toggle(
_open,
label,
GUI.skin.GetStyle("foldout"),
GUILayout.ExpandWidth(true),
GUILayout.Height(18)
);
if (_open)
{
action();
}
}, box);
open = _open;
}
private static Rect GUIRect(float width, float height)
{
return GUILayoutUtility.GetRect(width, height, GUILayout.ExpandWidth(width <= 0), GUILayout.ExpandHeight(height <= 0));
}
private static void Space(float space = 4f)
{
GUILayout.Space(space);
}
private static void LayoutH(System.Action action, bool box = false)
{
if (box)
{
GUIStyle style = new GUIStyle(GUI.skin.box);
GUILayout.BeginHorizontal(style);
}
else
{
GUILayout.BeginHorizontal();
}
action();
GUILayout.EndHorizontal();
}
private static void LayoutV(System.Action action, bool box = false)
{
if (box)
{
GUIStyle style = new GUIStyle(GUI.skin.box)
{
padding = new RectOffset(6, 6, 2, 2)
};
GUILayout.BeginVertical(style);
}
else
{
GUILayout.BeginVertical();
}
action();
GUILayout.EndVertical();
}
工程链接: 链接:https://pan.baidu.com/s/1xsA4-DlYh0jFCgrdj3UqKw 提取码:xj3y
想了解和学习更多unity相关知识可以关注下方公众号: