美文网首页
Unity: 一个简单的圆形遮罩组件

Unity: 一个简单的圆形遮罩组件

作者: Danny_Yan | 来源:发表于2019-07-30 14:33 被阅读0次

    代码如下:

    namespace DCG
    {
        using UnityEngine;
        using UnityEngine.UI;
        using UnityEngine.Sprites;
        using System.Collections.Generic;
    
        /// <summary>
        /// 圆形遮罩
        /// @Author: Danny Yan
        /// </summary>
        [RequireComponent(typeof(Image))]
        public class ImageMaskCircle : UnityEngine.UI.BaseMeshEffect
        {
            [SerializeField]
            private float offX = 0;
            [SerializeField]
            private float offY = 0;
    
            [SerializeField]
            [Tooltip("mask方式,true:视觉上mash相对位置不变,图片在mask内滑动  false:视觉上图片不动,mask在图片上滑动")]
            private bool _fixMaskViewPort = false;
            public bool fixMaskViewPort
            {
                get { return this._fixMaskViewPort; }
                set
                {
                    this._fixMaskViewPort = value;
                    this.graphic.SetVerticesDirty();
                }
            }
    
            public bool isInner = false;
    
            private Vector3[] vertices;
            private Vector3[] triangles;
    
            private Vector2 uvs;
    
            private Image _image;
    
            public float tickness = 1;
            public bool isFill = true;
    
            /** 半径 */
            [SerializeField]
            [Tooltip("半径")]
            private float _radius = 1;
            public float radius
            {
                get { return _radius; }
                set
                {
                    if (value < 0) value = 0;
                    this._radius = value;
                }
            }
            [SerializeField]
            [Tooltip("将圆切分为多少段")]
            [Range(3, 720)]
            private int _segment = 50;
            public int segment
            {
                get { return _segment; }
                set
                {
                    if (value < 3) value = 3;
                    else if (value > 720) value = 720;
                    this._segment = value;
                }
            }
            [SerializeField]
            [Tooltip("开口数-不绘制的段数")]
            private int _segmentOpen = 0;
            public int segmentOpen
            {
                get { return _segmentOpen; }
                set
                {
                    if (value < 0) value = 0;
                    else if (value > this._segment) value = this._segment;
                    this._segmentOpen = value;
                }
            }
    
            [Tooltip("开口起始角度")]
            public float openStartDeg = 0;
    
            [Tooltip("开口方向,是否为顺时针")]
            public bool openClockwise = true;
    
    
            // --------------------------------------------
            override protected void Awake()
            {
                base.Awake();
                this._image = this.GetComponent<Image>();
                var wh = _image.rectTransform.sizeDelta;
                this.radius = Mathf.Min(wh.x, wh.y) * .5f;
            }
    
            internal void FixedUpdate()
            {
    #if UNITY_EDITOR
                this.SetCircle(this.offX, this.offY, this.radius);
    #endif
            }
    
            public void SetCircle(float x, float y, float radius)
            {
                this.offX = x;
                this.offY = y;
                this.segmentOpen = _segmentOpen;
                var n = this._segment;
                this._radius = radius;
    
                if (this.isFill)
                {
                    var list = getCircleVertTri(radius);
                    this.vertices = list[0];
                    this.triangles = list[1];
                }
                else
                {
                    // 外圆
                    var list = this.getCircleVertTri(radius);
    
                    // 内圆
                    var _radius = radius - this.tickness;
                    if (_radius < 0) _radius = 0;
                    var list2 = this.getCircleVertTri(_radius);
    
                    // 连接2个圆的顶点
                    if (this.vertices == null || this.vertices.Length != n * 2)
                    {
                        this.vertices = new Vector3[n * 2];
                    }
    
                    if (this.triangles == null || this.triangles.Length != (n - this.segmentOpen) * 2)
                    {
                        this.triangles = new Vector3[(n - this.segmentOpen) * 2];
                    }
    
                    for (int i = 1, j = 0; i < list[0].Length; i++, j += 2)
                    {
                        this.vertices[j] = list[0][i]; // 外圆的第i个顶点
                        this.vertices[j + 1] = list2[0][i]; // 内圆的第i个顶点
                    }
    
                    // 以六边形为例,顶点索引为:
                    // (0,2,1)  (2,3,1)  (2,4,3) (4,5,3)
                    // (4,6,5)  (6,7,5)  (6,8,7) (8,9,7)
                    // (8,10,9)(10,11,9)(10,0,11)(0,1,11)
                    // 通过观察,得到以下规律
                    int a = 0, b = 0, c = 0;
                    for (int i = 0; i < triangles.Length; i++)
                    {
                        // j是在vertices中的索引
                        var j = this._segmentOpen * 2 + i;
                        if (j == vertices.Length - 1)
                        {
                            a = 0;
                        }
                        else
                        {
                            a = j % 2 == 0 ? j : j + 1;
                        }
                        // 到头部
                        b = j + 2 - (j + 2 >= vertices.Length ? vertices.Length : 0);
                        c = (int)Mathf.Floor(j / 2) * 2 + 1;
                        this.triangles[i] = new Vector3(a, b, c);
                    }
                }
    
                this.graphic.SetVerticesDirty();
            }
    
            private List<Vector3[]> getCircleVertTri(float radius)
            {
                var list = new List<Vector3[]>();
    
                // 每个内角大小
                float perAngle = 180f - (180f * (segment - 2) / segment);
                float perRadian = perAngle * Mathf.Deg2Rad;
    
                Vector3[] vertexList = new Vector3[segment + 1];
                Vector3[] triangleList = new Vector3[segment - this.segmentOpen];
    
                var _offx = this._fixMaskViewPort ? 0 : this.offX;
                var _offy = this._fixMaskViewPort ? 0 : this.offY;
    
                // 第一个顶点为圆心
                vertexList[0].x = _offx;
                vertexList[0].y = _offy;
                vertexList[0].z = 0;
    
                // 起始点角度
                float curDegree = this.openStartDeg;
    
                // 从起始角度开始计算出圆上的每个顶点
                for (int i = 1; i < vertexList.Length; i++)
                {
                    float ratian = curDegree * Mathf.Deg2Rad;
                    float _x3 = Mathf.Cos(ratian) * radius;
                    float _y3 = Mathf.Sin(ratian) * radius;
                    vertexList[i].x = _offx + _x3;
                    vertexList[i].y = _offy + _y3;
                    vertexList[i].z = 0;
    
                    curDegree -= perAngle * (this.openClockwise ? 1 : -1);
                }
    
                var ind = triangleList.Length - 1;
                for (int i = 0; i < triangleList.Length; i++)
                {
                    if (i == ind)
                    {
                        triangleList[i].x = 0;
                        triangleList[i].y = vertexList.Length - 1;
                        triangleList[i].z = 1;
                    }
                    else
                    {
                        // 剔除掉需要排除的段
                        triangleList[i].x = 0;
                        triangleList[i].y = this.segmentOpen + i + 1;
                        triangleList[i].z = this.segmentOpen + i + 2;
                    }
                }
    
                list.Add(vertexList);
                list.Add(triangleList);
                return list;
            }
    
            override public void ModifyMesh(VertexHelper vh)
            {
                if (!this.enabled || this._image == null) return;
                vh.Clear();
    
                float tw = _image.rectTransform.rect.width;
                float th = _image.rectTransform.rect.height;
                // uv映射得到宽高. GetOuterUV获取到包含所有像素的UV, GetInnerUV在遇到渐变图片时返回的uv可能大于1,最终会产生放大效果
                Vector4 uv = _image.overrideSprite != null ? (isInner ? DataUtility.GetInnerUV(_image.overrideSprite) : DataUtility.GetOuterUV(_image.overrideSprite)) : Vector4.zero;
                float w = uv.z - uv.x;
                float h = uv.w - uv.y;
    
                float uvScaleX = w / tw; // 宽的比例
                float uvScaleY = h / th; // 高的比例
                float uvCenterX = uv.x + w * _image.rectTransform.pivot.x;
                float uvCenterY = uv.y + h * _image.rectTransform.pivot.y;
    
                UIVertex uivert = UIVertex.simpleVert;
    
                for (int i = 0; i < (this.vertices == null ? 0 : this.vertices.Length); i++)
                {
                    uivert.color = _image.color;
                    uivert.position = this.vertices[i];
                    if (this._fixMaskViewPort)
                    {
                        this.uvs.x = (this.vertices[i].x + offX) * uvScaleX + uvCenterX;
                        this.uvs.y = (this.vertices[i].y + offY) * uvScaleY + uvCenterY;
                    }
                    else
                    {
                        this.uvs.x = this.vertices[i].x * uvScaleX + uvCenterX;
                        this.uvs.y = this.vertices[i].y * uvScaleY + uvCenterY;
                    }
                    uivert.uv0 = this.uvs;
                    vh.AddVert(uivert);
                }
    
                for (int i = 0; i < (this.triangles == null ? 0 : this.triangles.Length); i++)
                {
                    var tri = this.triangles[i];
                    vh.AddTriangle((int)tri.x, (int)tri.y, (int)tri.z);
                }
            }
        }
    }
    

    原始图像如下:


    image.png

    使用以下值:


    image.png
    得到:
    image.png

    转载请注册出处: https://www.jianshu.com/p/e8d6abb2f094

    相关文章

      网友评论

          本文标题:Unity: 一个简单的圆形遮罩组件

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