[Unity] UGUI拓展 - 实现Image的镜像

作者: _Walker__ | 来源:发表于2018-05-05 10:51 被阅读33次

参考:Unity3D UGUI优化:制作镜像图片(1)

效果预览 (左侧从上到下依次是Horizontal、Vertical、Quater的效果)

1、序

  这两天美术在整理UI资源,主要目的是降低内存,大概做的事有:

  • 删除废弃的资源
  • 重新划分图集(把小图集合并到大图集中)
  • 压缩大Icon:降低分辨率、对称资源砍半
      图片镜像就是为对称资源砍半提供的支持,更多的益处可以直接去看参考文章,里面写的很好了。我的实现方法和参考文章有很大差别,我主要希望能取得更好的性能,为此牺牲了一些代码量和表现效果。
参考文章实现 我的实现 勾选Preserve Aspect (上图是参考方案,下图是我的,虽然都不对但毕竟上面的好看Orz...)

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
}

相关文章

网友评论

    本文标题:[Unity] UGUI拓展 - 实现Image的镜像

    本文链接:https://www.haomeiwen.com/subject/aksjrftx.html