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

参考: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
}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352

推荐阅读更多精彩内容

  • 近期在维护知乎专栏,简书更新比较不即使,偷懒没写markdown,只粘贴内容过来好了。 代码部分粘贴过来比较乱,知...
    董夕阅读 5,127评论 0 5
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,649评论 18 139
  • 原文地址 http://www.fx114.net/qa-75-172454.aspx 使用Profiler工具...
    IongX阅读 5,848评论 1 11
  • 最近,不对,应该是近半年的时间,都在被自由职业,斜杠青年等热门名词充实着大脑。六个月前的自己,有冲动辞掉现有的工作...
    李聘2023阅读 405评论 0 0
  • 老婆有一架钢琴 但已久久没有触动 不是她的专业指法变得生疏 而是我日日夜夜的忙碌 耽搁了她的触景生情 相隔百里 隔...
    彩色屋顶阅读 105评论 2 0