[Unity] UGUI拓展 - 实现Image的镜像
2018-05-05 本文已影响33人
_Walker__
效果预览 (左侧从上到下依次是Horizontal、Vertical、Quater的效果)
1、序
这两天美术在整理UI资源,主要目的是降低内存,大概做的事有:
- 删除废弃的资源
- 重新划分图集(把小图集合并到大图集中)
- 压缩大Icon:降低分辨率、对称资源砍半等
图片镜像就是为对称资源砍半提供的支持,更多的益处可以直接去看参考文章,里面写的很好了。我的实现方法和参考文章有很大差别,我主要希望能取得更好的性能,为此牺牲了一些代码量和表现效果。
2、记
参考方案使用vh.GetUIVertexStream(output)
来获取顶点列表,它返回的顶点就已经是存在冗余的了,一个普通的Image其列表长度是6。也就是说,列表里每3个元素为一组,它们代表一个三角面。简单测试后发现,经过GetUIVertexStream() -> AddUIVertexTriangleStream()
这个处理后,即使图像没有发生显示上的变化,顶点数也会增加。
为了规避顶点增加的问题,我只是用了最简单的两个接口VertexHelper.AddVert(UIVertex v)
和VertexHelper.AddTriangle(int idx0, int idx1, int idx2)
,这可以保证产生的顶点数最少,因为相邻三角面的两个顶点是共用的。这个方案的弊端是没有普适的逻辑来创建三角面(或者有什么算法能做到,只是我不知道),因此只能人肉排顶点编号。对于目前只有几个三角面的情况,工作量和代码量都还在可接受范围内。
三角的面的顶点顺序,要遵循左手定则:
拇指朝向屏幕外,其余四指弯曲的方向,就是顶点的顺序
3、码
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 对UI图形进行镜像处理
/// Image - Sample顶点顺序
/// ------
/// |1 /2|
/// |0/ 3|
/// ------
/// </summary>
[RequireComponent(typeof(Image))]
public class UIImageMirror: BaseMeshEffect
{
public enum MirrorDir
{
Horizontal, // 水平镜像
Vertical, // 垂直镜像
Quater, // 四方镜像(先水平,后垂直)
}
protected const int AxisX = 0;
protected const int AxisY = 1;
[SerializeField]
private MirrorDir _direction;
public override void ModifyMesh(VertexHelper vh)
{
if (!IsActive()) return;
Image img = graphic as Image;
if (null == img) return;
if (img.type == Image.Type.Simple)
{
_SimpleMirror(vh);
}
}
#region ========= Image.Type.Simple模式 =========
private void _SimpleMirror(VertexHelper vh)
{
Rect rect = graphic.GetPixelAdjustedRect();
ShrinkVert(vh, rect);
Vector2 doubleCenter = rect.center * 2;
switch (_direction)
{
case MirrorDir.Horizontal:
SimpleMirrorHor(vh, doubleCenter.x);
break;
case MirrorDir.Vertical:
SimpleMirrorVer(vh, doubleCenter.y);
break;
case MirrorDir.Quater:
SimpleMirrorQuat(vh, doubleCenter);
break;
}
}
/// <summary>
/// Simple模式的水平镜像
/// 顶点布局:
/// -----------
/// |1 /2| \ 5|
/// |0/ 3| \4|
/// -----------
/// </summary>
protected void SimpleMirrorHor(VertexHelper vh, float doubleX)
{
AddMirrorVert(vh, 0, AxisX, doubleX); // 顶点4
AddMirrorVert(vh, 1, AxisX, doubleX); // 顶点5
vh.AddTriangle(2, 4, 3);
vh.AddTriangle(2, 5, 4);
}
/// <summary>
/// Simple模式的垂直镜像
/// 顶点布局:
/// ------
/// |4\ 5|
/// | \ |
/// ------
/// |1 /2|
/// |0/ 3|
/// ------
/// </summary>
protected void SimpleMirrorVer(VertexHelper vh, float doubleY)
{
AddMirrorVert(vh, 0, AxisY, doubleY); // 顶点4
AddMirrorVert(vh, 3, AxisY, doubleY); // 顶点5
vh.AddTriangle(2, 1, 4);
vh.AddTriangle(2, 4, 5);
}
/// <summary>
/// Simple模式的四方镜像
/// 顶点布局:
/// -----------
/// |6 /7| \ 8|
/// | / | \ |
/// -----------
/// |1 /2| \ 5|
/// |0/ 3| \4|
/// -----------
/// </summary>
protected void SimpleMirrorQuat(VertexHelper vh, Vector2 doubleCenter)
{
// 水平
AddMirrorVert(vh, 0, AxisX, doubleCenter.x); // 顶点4
AddMirrorVert(vh, 1, AxisX, doubleCenter.x); // 顶点5
vh.AddTriangle(2, 4, 3);
vh.AddTriangle(2, 5, 4);
// 垂直
AddMirrorVert(vh, 0, AxisY, doubleCenter.y); // 顶点6
AddMirrorVert(vh, 3, AxisY, doubleCenter.y); // 顶点7
AddMirrorVert(vh, 4, AxisY, doubleCenter.y); // 顶点8
vh.AddTriangle(7, 1, 6);
vh.AddTriangle(7, 2, 1);
vh.AddTriangle(7, 5, 2);
vh.AddTriangle(7, 8, 5);
}
#endregion
/// <summary>
/// 添加单个镜像顶点
/// </summary>
/// <param name="vh"></param>
/// <param name="srcVertIdx">镜像源顶点的索引值</param>
/// <param name="axis">轴向:0-X轴;1-Y轴</param>
/// <param name="doubleCenter">Rect.center轴向分量的两倍值</param>
protected static void AddMirrorVert(VertexHelper vh, int srcVertIdx, int axis, float doubleCenter)
{
UIVertex vert = UIVertex.simpleVert;
vh.PopulateUIVertex(ref vert, srcVertIdx);
Vector3 pos = vert.position;
pos[axis] = doubleCenter - pos[axis];
vert.position = pos;
vh.AddVert(vert);
}
/// <summary>
/// 收缩顶点坐标
/// 根据镜像类型,将原始顶点坐标向“起始点(左/下)”收缩
/// </summary>
protected void ShrinkVert(VertexHelper vh, Rect rect)
{
int count = vh.currentVertCount;
UIVertex vert = UIVertex.simpleVert;
for (int i = 0; i < count; ++i)
{
vh.PopulateUIVertex(ref vert, i);
Vector3 pos = vert.position;
if (MirrorDir.Horizontal == _direction || MirrorDir.Quater == _direction)
{
pos.x = (rect.x + pos.x) * 0.5f;
}
if (MirrorDir.Vertical == _direction || MirrorDir.Quater == _direction)
{
pos.y = (rect.y + pos.y) * 0.5f;
}
vert.position = pos;
vh.SetUIVertex(vert, i);
}
}
#region ======设置Image的原尺寸======
private RectTransform _rectTrans;
public RectTransform RectTrans
{
get
{
if (null == _rectTrans)
{
_rectTrans = GetComponent<RectTransform>();
}
return _rectTrans;
}
}
public void SetNativeSize()
{
Image img = graphic as Image;
if (null == img) return;
Sprite sprite = img.overrideSprite;
if(null == sprite) return;
float w = sprite.rect.width / img.pixelsPerUnit;
float h = sprite.rect.height / img.pixelsPerUnit;
RectTrans.anchorMax = RectTrans.anchorMin;
switch (_direction)
{
case MirrorDir.Horizontal:
RectTrans.sizeDelta = new Vector2(w * 2, h);
break;
case MirrorDir.Vertical:
RectTrans.sizeDelta = new Vector2(w, h * 2);
break;
case MirrorDir.Quater:
RectTrans.sizeDelta = new Vector2(w * 2, h * 2);
break;
}
img.SetVerticesDirty();
}
#endregion
}