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; } }