自己使用顶点描绘圆形图片

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

//保证图片的宽高比与Image的宽高比一致,方便图像uv与物体的对应关系,否则图片会变形
public class CircleImage : Image
{
    /// <summary>
    /// 圆形有多少个面
    /// </summary>
    public int segments = 100;
    public float fillPrecent = 1f;
    private List<Vector3> listVertex;

    protected override void OnPopulateMesh(VertexHelper toFill)
    {
        toFill.Clear();
        float width = rectTransform.rect.width;
        float height = rectTransform.rect.height;
        if (width == 0 || height == 0 || segments == 0)
        {
            return;
        }
        Color32 col = new Color32(60, 60, 60, 255);
        /*  1.获得uv
         *  2.计算uv宽高,中心点,uv与物体宽高的换算系数
         *  3.计算每个三角面的弧度,圆的半径
         *  4.计算各个三角面的顶点
         *  5.生成三角形
        //*/
        //获得uv
        Vector4 uv = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
        
        //计算uv宽高,中心点,uv与物体宽高的换算系数
        float uvW = uv.z - uv.x;
        float uvH = uv.w - uv.y;
        Vector2 uvCenter = new Vector2(uvW * 0.5f, uvH * 0.5f);
        Vector2 converRatio = new Vector2(uvW / width, uvH / height);

        //计算每个三角面的弧度,圆的半径
        float radian = (2 * Mathf.PI) / segments;
        float radius = width < height ? width * 0.5f : height * 0.5f;

        //计算各个三角面的顶点
        Vector2 originPos = new Vector2((0.5f - rectTransform.pivot.x) * width, (0.5f - rectTransform.pivot.y) * height);
        Vector2 vertPos = Vector2.zero;
        UIVertex originVert = new UIVertex(); //中心点顶点
        byte temp = (byte)(255 * fillPrecent);
        originVert.color = new Color32(temp, temp, temp, 255);
        originVert.position = originPos;
        originVert.uv0 = new Vector2(vertPos.x * converRatio.x + uvCenter.x, vertPos.y * converRatio.y + uvCenter.y);
        toFill.AddVert(originVert);
        //计算圆边缘的顶点
        int realSegment = (int)(fillPrecent * segments) + 1;
        int vertCount = segments + 1;
        float curRadian = 0;
        listVertex = new List<Vector3>(vertCount);
        for (int i = 0; i < vertCount; i++)
        {
            float x = Mathf.Cos(curRadian) * radius;
            float y = Mathf.Sin(curRadian) * radius;
            curRadian += radian;
            UIVertex vertTemp = new UIVertex();
            if (i < realSegment && fillPrecent > 0)
            {
                vertTemp.color = color;
            }
            else 
            {
                vertTemp.color = col;
            }
            vertPos = new Vector2(x, y);
            vertTemp.position = vertPos + originPos;
            vertTemp.uv0 = new Vector2(vertPos.x * converRatio.x + uvCenter.x, vertPos.y * converRatio.y + uvCenter.y);
            toFill.AddVert(vertTemp);
            listVertex.Add(vertPos + originPos);
        }

        //生成三角形
        int id = 1;
        for (int i = 0; i < segments; i++)
        {
            toFill.AddTriangle(id, 0, id + 1);
            id++;
        }
    }

    //判断点击是否有效
    public override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
    {
        RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out Vector2 localPoint);
        return MIsRaycastLocationValid(localPoint, listVertex);
    }
    //判断方法:从点击点位置向右画线,查看与相邻顶点组成的线段的交点个数是奇数还是偶数,奇数在范围内,偶数不在范围内
    private bool MIsRaycastLocationValid(Vector2 point, List<Vector3> listVert)
    {
        int count = 0;
        //遍历所有相邻顶点组成的线段
        for (int i = 0; i < listVert.Count; i++)
        {
            Vector3 v1 = listVert[i];
            Vector3 v2 = listVert[(i + 1) % listVert.Count];
            if (v1.x == v2.x && v1.y == v2.y)
            {
                continue;
            }
            //获取有效范围内的线段
            if (IsYRange(v1, v2, point))
            {
                count++;
            }
        }
        Debug.Log(count);
        return (count % 2) == 1;
    }
    //用来判断y是否在v1,v2的线段内
    private bool IsYRange(Vector3 v1, Vector3 v2, Vector2 point)
    {
        if (v1.y > v2.y)
        {
            if (point.y >= v2.y && point.y <= v1.y)
            {
                return IsRectangleContainsScreenPoint(v1, v2, point);
            }
        }
        else
        {
            if (point.y >= v1.y && point.y <= v2.y)
            {
                return IsRectangleContainsScreenPoint(v1, v2, point);
            }
        }
        return false;
    }

    //判断point点向右画线是否与v1、v2的线段相交
    private bool IsRectangleContainsScreenPoint(Vector3 v1, Vector3 v2, Vector2 point)
    {
        //y=kx+b, k = (y2-y1)/(x2-x1)
        float k = (v1.y - v2.y) / (v1.x - v2.x);
        float x = v1.x - (v1.y - point.y) / k;
        return point.x <= x;
    }
}
原文地址:https://www.cnblogs.com/luoyanghao/p/15130235.html