Unity Image镜像案例

效果:
Unity Image镜像案例_第1张图片

Unity Image镜像案例_第2张图片

BaseMeshEffect介绍:
要做镜像,就要修改Image的网格,所以要继承BaseMeshEffect,实现修改网格的抽象方法。

BaseMeshEffect是UGUI源码中的东西,如下:
Unity Image镜像案例_第3张图片

graphic是当前的图形,可以转成Image,表示当前脚本上的Image。

ModifyMesh(VertexHelper vh)就是我们要实现的抽象方法了,用来执行修改网格的操作。

具体实现过程如下:

先写个翻转顶点和剔除无效顶点的工具类

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class MirrorUtil 
{
    /// 
    /// 根据MirrorType来翻转顶点
    /// 
    public static void MirrorVertex(Rect rect, List vertices,MirrorType mirrorType)
    {
        int count = vertices.Count;

        switch (mirrorType)
        {
            case MirrorType.HORIZONTAL:
                ExtendLength(vertices, count);
                MirrorVertex(rect, vertices, count, MirrorHorizontal);
                break;
            case MirrorType.VERTICAL:
                ExtendLength(vertices, count);
                MirrorVertex(rect, vertices, count, MirrorVertical);
                break;
            case MirrorType.ALL:
                ExtendLength(vertices, count * 3);
                MirrorVertex(rect, vertices, count, MirrorHorizontal);
                MirrorVertex(rect, vertices, count * 2, MirrorVertical);
                break;
            default:
                throw new ArgumentOutOfRangeException(nameof(mirrorType), mirrorType, null);
        }
    }

    /// 
    /// 翻转顶点
    /// 
    private static void MirrorVertex(Rect rect, List vertices,int count,Func getPos)
    {
        UIVertex vertex;
        for (int i = 0; i < count; i++)
        {
            vertex = vertices[i];
            vertex.position = getPos(rect, vertex.position);
            vertices.Add(vertex);
        }
    }

    /// 
    /// 横向翻转顶点
    /// 
    private static Vector3 MirrorHorizontal(Rect rect, Vector3 pos)
    {
        return new Vector3(rect.center.x * 2 - pos.x,pos.y,pos.z);
    }
    /// 
    /// 纵向翻转顶点
    /// 
    private static Vector3 MirrorVertical(Rect rect, Vector3 pos)
    {
        return new Vector3(pos.x,rect.center.y * 2 - pos.y,pos.z);
    }

    /// 
    /// 扩展指定List的长度,因为当List容量不足时,默认的List的长度是翻倍增加的
    /// 
    /// 
    /// 
    private static void ExtendLength(List vertices,int addLength)
    {
        int length = vertices.Count + addLength;
        if (vertices.Capacity < length)
        {
            vertices.Capacity = length;
        }
    }

    /// 
    /// 剔除无效顶点
    /// 
    /// 
    public static void RemoveInvalidVertex(List vertices)
    {
        int count = vertices.Count;

        int i = 0;

        while (i < count)
        {
            UIVertex v1 = vertices[i];
            UIVertex v2 = vertices[i+1];
            UIVertex v3 = vertices[i+2];

            if (v1.position == v2.position
                || v2.position == v3.position
                || v3.position == v1.position)
            {
                vertices[i] = vertices[count - 3];
                vertices[i + 1] = vertices[count - 2];
                vertices[i+2] = vertices[count - 1];
                count -= 3;
            }
            else
            {
                i += 3;
            }
        }

        if (count < vertices.Count)
        {
            //移除指定范围内的顶点,即剔除掉无效顶点
            vertices.RemoveRange(count,vertices.Count - count);
        }
    }
}

Unity Image镜像案例_第4张图片

写个编辑器扩展类,用于扩展图片设置大小的功能

using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(MirrorImage))]//在MirrorImage脚本上添加编辑器扩展功能
[CanEditMultipleObjects]//选择多个物体的时候也可以操作
public class MirrorImageEditor : Editor
{
    private MirrorImage _mirrorImage;
    private void OnEnable()
    {
        //取得MirrorImage脚本
        _mirrorImage = serializedObject.targetObject as MirrorImage;
    }

    /// 
    /// 对该方法的重写,可以自定义对Inspector面板的绘制
    /// 
    public override void OnInspectorGUI()
    {
        //更新序列化物体
        serializedObject.Update();
        //枚举弹出选择菜单
        _mirrorImage._mirrorType = (MirrorType)EditorGUILayout.EnumPopup("镜像类型",_mirrorImage._mirrorType);
        if(GUILayout.Button("设置默认图片大小"))
        {
            //记录RectTransform的操作,不记录的话,无法执行ctrl+z的撤回操作
            Undo.RecordObject(_mirrorImage.transform.GetComponent(),"");
            _mirrorImage.SetNativeSize();
        }

        if (GUI.changed)
        {
            EditorUtility.SetDirty(target);
            _mirrorImage.SetVerticesDirty();
        }
        //应用修改的属性
        serializedObject.ApplyModifiedProperties();
    }
}

新建一个MirrorImage.cs脚本,挂载在Image上,代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

/// 
/// 镜像接口
/// 
public interface IMirror
{
    bool CanDraw { get; }
    void Init(MirrorType mirrorType, Image image);
    void Draw(List vertices);
}

[RequireComponent(typeof(Image))]
public class MirrorImage : BaseMeshEffect
{
    //镜像的类型
    public MirrorType _mirrorType = MirrorType.HORIZONTAL;
    //存放UI的顶点信息
    private List _vertices = new List();
    //key是图片平铺类型
    private Dictionary _mirrors;

    private Dictionary Mirrors
    {
        get
        {
            if (_mirrors == null)
            {
                InitMirror();
            }

            return _mirrors;
        }
    }

    /// 
    /// 初始化镜像Image的类型字典
    /// 
    private void InitMirror()
    {
        _mirrors = new Dictionary();
        _mirrors.Add(Image.Type.Simple,new SimpleMirror());
        _mirrors.Add(Image.Type.Sliced,new SimpleMirror());
        _mirrors.Add(Image.Type.Tiled,new TiledMirror());
    }
    
    /// 
    /// 修改网格
    /// 
    /// 
    public override void ModifyMesh(VertexHelper vh)
    {
        //如果当前脚本隐藏了,就执行默认的,变回原来的样子,不加这两句是不会变化的
        if (!IsActive())
            return;
        IMirror mirror = GetMirrorObject();
        //取得UI网格的顶点
        vh.GetUIVertexStream(_vertices);
        //
        mirror.Draw(_vertices);
        vh.Clear();
        vh.AddUIVertexTriangleStream(_vertices);
    }

    /// 
    /// 获取Mirror的实现类
    /// 
    /// 
    private IMirror GetMirrorObject()
    {
        Image image = graphic as Image;
        IMirror mirror = Mirrors[Image.Type.Simple];
        if (Mirrors.ContainsKey(image.type))
        {
            mirror = Mirrors[image.type];
            mirror.Init(_mirrorType,image);
            if (mirror.CanDraw)
            {
                return mirror;
            }
        }
        
        mirror.Init(_mirrorType,image);
        return mirror;
    }

    /// 
    /// 扩大显示区域大小
    /// 
    public void SetNativeSize()
    {
        Image image = graphic as Image;
        if (image.overrideSprite != null)
        {
            //pixelsPerUnit表示Unity中一个单位对应这个图片的多少像素
            float width = image.overrideSprite.rect.width / image.pixelsPerUnit;
            float height = image.overrideSprite.rect.height / image.pixelsPerUnit;
            //Unity中点击SetNativeSize后,anchorMax的数值会变得跟anchorMin一样
            image.rectTransform.anchorMax = image.rectTransform.anchorMin;
            Vector2 temp = Vector2.zero;
            switch (_mirrorType)
            {
                case MirrorType.HORIZONTAL:
                    temp.x = width * 2;
                    temp.y = height;
                    break;
                case MirrorType.VERTICAL:
                    temp.x = width;
                    temp.y = height * 2;
                    break;
                case MirrorType.ALL:
                    temp.x = width * 2;
                    temp.y = height * 2;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            image.rectTransform.sizeDelta = temp;
            image.SetVerticesDirty();
        }
    }
    /// 
    /// 设置脏标志,才能更新UI
    /// 
    public void SetVerticesDirty()
    {
        Image image = graphic as Image;
        image.SetVerticesDirty();
    }
}

/// 
/// 镜像类型,包括横向,纵向,横纵向
/// 
public enum MirrorType
{
    HORIZONTAL,
    VERTICAL,
    ALL
}

新建一个镜像接口的实现类SimpleMirror.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class SimpleMirror : IMirror
{
    private MirrorType _mirrorType;
    private Image _image;
    public bool CanDraw
    {
        get { return true; }
    }

    public void Init(MirrorType mirrorType, Image image)
    {
        _mirrorType = mirrorType;
        _image = image;
    }

    /// 
    /// 绘制图形
    /// 
    /// 
    public void Draw(List vertices)
    {
        //目标绘制区域的坐标及大小(x,y为该UI相对于轴心的坐标,width,height为UI宽高)
        Rect rect = _image.GetPixelAdjustedRect();
        //将顶点位置变换到左上角
        ChangeVertexPos(rect, vertices);
        //移除无效顶点
        MirrorUtil.RemoveInvalidVertex(vertices);
        //根据MirrorType来翻转顶点
        MirrorUtil.MirrorVertex(rect,vertices,_mirrorType);
    }

    /// 
    /// 将图像分成四份,然后图像变换到左上角的位置
    /// 
    /// 
    /// 
    private void ChangeVertexPos(Rect rect, List vertices)
    {
        Vector3 pos = Vector3.zero;
        UIVertex uiVertex;
        for (int i = 0; i < vertices.Count; i++)
        {
            uiVertex = vertices[i];
            pos = uiVertex.position;
            if (_mirrorType == MirrorType.HORIZONTAL || _mirrorType == MirrorType.ALL)
            {
                pos.x = (pos.x + rect.x) * 0.5f;
            }

            if (_mirrorType == MirrorType.VERTICAL || _mirrorType == MirrorType.ALL)
            {
                //不加上+ rect.height的话,y方向的值变换后就在左下角了
                pos.y = (pos.y + rect.y + rect.height) * 0.5f;
            }

            uiVertex.position = pos;
            vertices[i] = uiVertex;
        }
    }
}

Unity Image镜像案例_第5张图片

再新建一个镜像接口的实现类TiledMirror.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Sprites;
using UnityEngine.UI;

public class TiledMirror : IMirror
{

    private MirrorType _mirrorType;
    private Image _image;

    /// 
    /// 判断是否可以绘制
    /// 
    public bool CanDraw
    {
        get { return _image.overrideSprite != null; }
    }

    public void Init(MirrorType mirrorType, Image image)
    {
        _mirrorType = mirrorType;
        _image = image;
    }

    /// 
    /// 绘制翻转后的UV
    /// 
    /// 
    public void Draw(List vertices)
    {
        Sprite sprite = _image.overrideSprite;
        //获得sprite的uv坐标
        Vector4 uv = DataUtility.GetOuterUV(sprite);

        float width = sprite.rect.width / _image.pixelsPerUnit;
        float height = sprite.rect.height / _image.pixelsPerUnit;
        //目标绘制区域的坐标及大小(x,y为该UI相对于轴心的坐标,width,height为UI宽高)
        Rect rect = _image.GetPixelAdjustedRect();

        for (int i = 0; i < vertices.Count /3; i++)
        {
            UIVertex v1 = vertices[i * 3];
            UIVertex v2 = vertices[i* 3+1];
            UIVertex v3 = vertices[i* 3+2];

            Vector2 center = GetCenter(v1.position, v2.position, v3.position);

            if (_mirrorType == MirrorType.HORIZONTAL || _mirrorType == MirrorType.ALL)
            {
                //用来判断属于哪个区域块
                int id = Mathf.CeilToInt((center.x - rect.xMin) / width);
                //偶数区域才需要翻转UV
                if (id % 2 == 0)
                {
                    v1.uv0 = GetFlipUV(v1.uv0, uv, true);
                    v2.uv0 = GetFlipUV(v2.uv0, uv, true);
                    v3.uv0 = GetFlipUV(v3.uv0, uv, true);
                }
            }
            
            if (_mirrorType == MirrorType.VERTICAL || _mirrorType == MirrorType.ALL)
            {
                //用来判断属于哪个区域块
                int id = Mathf.CeilToInt((center.y - rect.yMin) / height);
                //偶数区域才需要翻转UV
                if (id % 2 == 0)
                {
                    v1.uv0 = GetFlipUV(v1.uv0, uv, false);
                    v2.uv0 = GetFlipUV(v2.uv0, uv, false);
                    v3.uv0 = GetFlipUV(v3.uv0, uv, false);
                }
            }
            vertices[i* 3] = v1;
            vertices[i* 3+1] = v2;
            vertices[i* 3+2] = v3;
        }
    }

    /// 
    /// 获取三个坐标点的中心
    /// 
    /// 
    private Vector2 GetCenter(Vector3 p1,Vector3 p2,Vector3 p3)
    {
        float x = GetCenterValue(p1.x, p2.x, p3.x);
        float y = GetCenterValue(p1.y, p2.y, p3.y);
        return new Vector2(x,y);
    }

    private float GetCenterValue(float f1,float f2,float f3)
    {
        float min = Mathf.Min(Mathf.Min(f1, f2), f3);
        float max = Mathf.Max(Mathf.Max(f1, f2), f3);
        return (min + max) * 0.5f;
    }

    /// 
    /// UV翻转
    /// 
    /// 
    private Vector2 GetFlipUV(Vector2 uv,Vector4 outerUv,bool isHorizontal)
    {
        //outerUv的左下角坐标为(x,y),右上角坐标为(z,w)
        if (isHorizontal)
        {
            uv.x = outerUv.x + outerUv.z - uv.x;
        }
        else
        {
            uv.y = outerUv.y + outerUv.w - uv.y;
        }

        return uv;
    }
}

你可能感兴趣的:(Unity,游戏开发,unity,unity3d)