using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
///
/// UGUI描边
///
public class OutlineEx : BaseMeshEffect
{
[Header("新shader描边")]
public Color OutlineColor = new Color(0, 0, 0, 0.5f);// Color.black;
QualityTextColType m_ColorType = QualityTextColType.None;
public QualityTextColType colorType
{
get { return m_ColorType; }
set
{
if (m_ColorType != value)
{
m_ColorType = value;
OutlineColor = UIHelper.GetUIOutlineColor(value);
this._Refresh();
}
}
}
[Range(0, 30)]
//默认10,不同的颜色(包括调整alpha)视觉上描边粗细有差异,故shader里乘以0.2适应不同情况
public int OutlineWidth = 10;
private static List m_VetexList = new List();
// 材质池:Key为"颜色_宽度"格式的字符串,Value为对应的材质; 减少合批问题,又能保持不同的描边;共用材质会同时改变参数
private static Dictionary m_MaterialPool = new Dictionary();
static Shader m_tmpShader;
private static Shader m_Shader
{
get
{
if (m_tmpShader == null)
{
m_tmpShader = Shader.Find("TSF Shaders/UI/OutlineEx");
}
return m_tmpShader;
}
}
protected override void Start()
{
base.Start();
// 使用材质池获取或创建材质
string key = GetMaterialKey(OutlineColor, OutlineWidth);
if (!m_MaterialPool.TryGetValue(key, out Material material))
{
material = new Material(m_Shader);
material.SetColor("_OutlineColor", this.OutlineColor);
material.SetInt("_OutlineWidth", this.OutlineWidth);
m_MaterialPool[key] = material;
}
base.graphic.material = material;
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 OnEnable()
// {
// base.OnEnable();
// this._Refresh();
// }
protected override void OnValidate()
{
base.OnValidate();
if (base.graphic.material != null)
{
this._Refresh();
}
}
#endif
private void _Refresh()
{
// 检查当前材质是否与所需属性匹配,如果不匹配则从池中获取或创建新材质
string key = GetMaterialKey(OutlineColor, OutlineWidth);
Material material;
if (!m_MaterialPool.TryGetValue(key, out material))
{
// 如果池中没有对应的材质,创建新材质
var shader = Shader.Find("TSF Shaders/UI/OutlineEx");
material = new Material(shader);
m_MaterialPool[key] = material;
}
if (material == null)
{
// 防范材质被删的情况,创建新材质
material = new Material(m_Shader);
m_MaterialPool[key] = material;
}
material.SetColor("_OutlineColor", this.OutlineColor);
material.SetInt("_OutlineWidth", this.OutlineWidth);
// 更新图形材质
base.graphic.material = material;
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];
// 计算原顶点坐标中心点
//
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;
// 计算原始顶点坐标和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);
// 为每个顶点设置新的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);
// 应用设置后的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 += new Vector4((pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1)).x, (pUVX / pTriangleX.magnitude * posXOffset * (Vector2.Dot(pTriangleX, Vector2.right) > 0 ? 1 : -1)).y, 0, 0);
uv += new Vector4((pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1)).x, (pUVY / pTriangleY.magnitude * posYOffset * (Vector2.Dot(pTriangleY, Vector2.up) > 0 ? 1 : -1)).y, 0, 0);
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));
}
// 生成材质池的键
private string GetMaterialKey(Color color, int width)
{
// 使用颜色的RGBA值和宽度生成唯一键
return string.Format("{0}_{1}_{2}_{3}_{4}",
color.r, color.g, color.b, color.a, width);
}
}