Runtime类图分析
Shadow
Shadow继承自BaseMeshEffect,BaseMeshEffect继承自UIBehaviour, IMeshModifier。Shadow通过为图像或者文字的Mesh添加顶点来实现阴影效果,而Outline是在对象四角上各添加了一个Shadow。由此我们可以得知,Outline的(额外)消耗是Shadow的四倍,所以还是需要谨慎使用。
ModifyMesh方法
调用VertexHelper的GetUIVertexStream,在此方法中,调用CanvasRenderer.CreateUIVertexStream方法,获取UIVertex流(顶点数据)
调用ApplyShadow,实际上调用的是ApplyShadowZeroAlloc方法,在此方法中根据effectColor和effectDistance设置,从头到尾复制顶点,并将它们转换为给定偏移量的阴影(顶点数据)。
调用VertexHelper的Clear方法,清除Stream中的所有顶点数据。
调用VertexHelper的AddUIVertexTriangleStream,向Stream中添加三角形列表,在此方法中,调用CanvasRenderer.SplitUIVertexStreams方法,生成上述顶点数据生成的三角形。
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方法
遍历所有顶点获取其位置,然后根据指定的阴影颜色和偏移量计算出新生成的顶点颜色和位置信息。
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;
}
可以在Wireframe模式下看到它的生成
EffectColor
阴影颜色
EffectDistance
生成阴影偏移量
(1)x轴水平方向:当x=0时,阴影在水平方向上中间;当x>0时,阴影在右方;当x<0时,阴影在左方
(2)y轴垂直方向:当y=0时,阴影在垂直方向上中间;当y>0时,阴影在上方;当y<0时,阴影在下方
x、y的范围均为[-600,600]
private const float kMaxEffectDistance = 600f;
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;
if (graphic != null)
graphic.SetVerticesDirty();
}
}
Use Graphic Alpha
是否使用Graphic中的Alpha透明度
勾选之后阴影的透明度会根据Graphic的透明度变化而变化
BaseMeshEffect
是一个抽象类,实现了IMeshModifier接口,接口允许在传递给CanvasRenderer前,修改图形的顶点,从而修改实现这个接口的GameObject的Mesh。ModifyMesh是一个抽象方法,会在子类中实现。它的OnEnable、OnDisable和OnDidApplyAnimationProperties(当应用动画属性时),会调用Graphic的SetVerticesDirty方法(设置顶点数据为Dirty,重建图像时重新生成顶点数据)。
Outline
继承自Shadow,实现了IMeshModifier接口的ModifyMesh方法。
ModifyMesh方法
跟shadow的ModifyMesh方法相比,在四个角上各添加了一组Shadow顶点。
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive())
return;
var verts = ListPool<UIVertex>.Get();
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);
ListPool<UIVertex>.Release(verts);
}
可以看到同一个组件添加Shadow和Outline组件之后的对比
PositionAsUV1
根据坐标点设置uv1坐标(法线贴图坐标),为图片或者文字添加法线贴图效果。
ModifyMesh方法
public override void ModifyMesh(VertexHelper vh)
{
UIVertex vert = new UIVertex();
for (int i = 0; i < vh.currentVertCount; i++)
{
vh.PopulateUIVertex(ref vert, i);
vert.uv1 = new Vector2(vert.position.x, vert.position.y);
vh.SetUIVertex(vert, i);
}
}