相信很多人在刚入职Unity的时候都被告诫过尽量避免使用OutLine,只知道它很费性能,但是很多人并不知道它为什么很费性能。今天通过源码来探索一下。
首先看一下OutLine.cs里的源码
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
var verts = ListPool<UIVertex>.Get();//这里的列表使用了对象池,减少消耗
vh.GetUIVertexStream(verts);
//这里直接把顶点容量扩大到了5倍
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);
ListPool<UIVertex>.Release(verts);
}
我们在 var neededCpacity = verts.Count * 5;这一句中就可以发现,直接把顶点容量扩大到5倍,也就是比之前增加了4倍。
我们发现它调用了4次 ApplyShadowZeroAlloc 方法,这个方法在Shadow.cs里面,因为OutLine继承了Shadow。接下来看一下ApplyShadowZeroAlloc方法的具体内容。
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;
if (m_UseGraphicAlpha)
newColor.a = (byte)((newColor.a * verts[i].color.a) / 255);
vt.color = newColor;
verts[i] = vt;
}
}
ApplyShadowZeroAlloc这里面就是去复制顶点添加到原来的顶点列表,然后给复制的来的顶点去设置偏移和颜色。
由此可见,使用了OutLine后就相当于直接多出4倍的顶点,所以要尽量避免使用。
然后我就想,如果用两倍的顶点,一倍顶点去放大一圈,一倍顶点去缩小一圈,能不能也实现对应的效果。然后我就写了一个新的OutLine组件。
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class OutLineNew : Shadow
{
private const int VERTEX_GROUP_NUM = 6;
private enum EDirection
{
LeftTop,
RightTop,
LeftBottom,
RightBottom,
}
protected OutLineNew()
{ }
private void SetVertexPos( List<UIVertex> vertexs ,int index ,bool isBig , EDirection dir )
{
UIVertex vertex = vertexs[index];
Vector3 pos = vertex.position;
int parm = isBig ? 1 : -1;
switch (dir)
{
case EDirection.LeftTop:
pos.x -= effectDistance.x * parm;
pos.y += effectDistance.y * parm;
break;
case EDirection.RightTop:
pos.x += effectDistance.x * parm;
pos.y += effectDistance.y * parm;
break;
case EDirection.LeftBottom:
pos.x -= effectDistance.x * parm;
pos.y -= effectDistance.y * parm;
break;
case EDirection.RightBottom:
pos.x += effectDistance.x * parm;
pos.y -= effectDistance.y * parm;
break;
default:
break;
}
vertex.position = pos;
vertexs[index] = vertex;
}
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
List<UIVertex> verts = ListPool<UIVertex>.Get();
vh.GetUIVertexStream(verts);
//把顶点容量扩大到原来的3倍
var neededCpacity = verts.Count * 3;
if (verts.Capacity < neededCpacity)
verts.Capacity = neededCpacity;
int cnt = verts.Count;
//给顶点设置阴影颜色
for (int i = 0; i < cnt; i++)
{
var vt = verts[i];
verts.Add(vt);
vt.color = effectColor;
verts[i] = vt;
}
for (int i = cnt; i < cnt*2; i++)
{
var vt = verts[i];
verts.Add(vt);
vt.color = effectColor;
verts[i] = vt;
}
//给顶点设置偏移。(一个文字是有4个顶点,组成两个三角面,但是组成两个三角面时,有两个顶点会重复)
//先给一倍的顶点去放大一圈
for (int i = 0; i < cnt; i+= VERTEX_GROUP_NUM)
{
SetVertexPos(verts,i, true , EDirection.LeftTop);
SetVertexPos(verts, i+1, true, EDirection.RightTop);
SetVertexPos(verts, i+2, true, EDirection.RightBottom);
SetVertexPos(verts, i+3, true, EDirection.RightBottom);
SetVertexPos(verts, i+4, true, EDirection.LeftBottom);
SetVertexPos(verts, i+5, true, EDirection.LeftTop);
}
//再给一倍的顶点去缩小一圈
for (int i = cnt; i < cnt*2; i += VERTEX_GROUP_NUM)
{
SetVertexPos(verts, i, false, EDirection.LeftTop);
SetVertexPos(verts, i + 1, false, EDirection.RightTop);
SetVertexPos(verts, i + 2, false, EDirection.RightBottom);
SetVertexPos(verts, i + 3, false, EDirection.RightBottom);
SetVertexPos(verts, i + 4, false, EDirection.LeftBottom);
SetVertexPos(verts, i + 5, false, EDirection.LeftTop);
}
vh.Clear();
vh.AddUIVertexTriangleStream(verts);
ListPool<UIVertex>.Release(verts);
}
}
对比一下效果
~~~~~果然效果差多了呀~~
顺便再说一下Shadow这个组件吧。看完了OutLine,Shadow就不难理解了。先上源码:
protected void ApplyShadow(List<UIVertex> verts, Color32 color, int start, int end, float x, float y)
{
ApplyShadowZeroAlloc(verts, color, start, end, x, y);
}
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
var output = ListPool<UIVertex>.Get();
vh.GetUIVertexStream(output);
ApplyShadow(output, effectColor, 0, output.Count, effectDistance.x, effectDistance.y);
vh.Clear();
vh.AddUIVertexTriangleStream(output);
ListPool<UIVertex>.Release(output);
}
它就是调用了一次ApplyShadowZeroAlloc,多生成了一倍的顶点。
总结一下就是,OutLine会额外生成4倍的顶点,Shadow会额外生成1倍的顶点。
好了今天就到这吧