using System.Collections.Generic; using UnityEngine; using UnityEngine.Sprites; using UnityEngine.UI; [AddComponentMenu("UI/Custom/Circle Image")] [RequireComponent(typeof(Image))] public class CircleImage : BaseMeshEffect, ICanvasRaycastFilter { [Range(0, 1)] [SerializeField] float m_FillPercent = 1f; public float fillPercent { get { return m_FillPercent; } } [SerializeField] private bool m_Reversed = false; public bool reversed { get { return m_Reversed; } } [SerializeField] Align m_Align = Align.Top; public Align align { get { return m_Align; } } [SerializeField] bool m_FullFill = true; public bool fullFill { get { return m_FullFill; } } [Range(0, 1)] [SerializeField] float m_Thickness = 0f; public float thickness { get { return m_Thickness; } set { m_Thickness = value; } } [Range(4, 128)] [SerializeField] int m_Segements = 30; public int segements { get { return m_Segements; } } private Image m_Image; public Image image { get { return m_Image ?? (m_Image = this.AddMissingComponent()); } } private List innerVertices; private List outterVertices; protected override void Awake() { innerVertices = new List(); outterVertices = new List(); } public override void ModifyMesh(VertexHelper vh) { vh.Clear(); innerVertices.Clear(); outterVertices.Clear(); var degreeDelta = (float)(2 * Mathf.PI / segements); var realSegements = (int)(segements * fillPercent); var width = image.rectTransform.rect.width; var height = image.rectTransform.rect.height; var outerArea = new Vector2(0.5f * width, 0.5f * height); var innerArea = outerArea - thickness * outerArea; var uv = image.overrideSprite != null ? DataUtility.GetOuterUV(image.overrideSprite) : Vector4.zero; var uvCenterX = (uv.x + uv.z) * 0.5f; var uvCenterY = (uv.y + uv.w) * 0.5f; var uvScaleX = (uv.z - uv.x) / width; var uvScaleY = (uv.w - uv.y) / height; UIVertex uiVertex; var degree = Mathf.PI * 0.5f * (int)align; var position = Vector2.zero; var uv0 = Vector2.zero; var verticeCount = 0; var triangleCount = 0; if (fullFill) { verticeCount = realSegements + 1; uv0 = new Vector2(position.x * uvScaleX + uvCenterX, position.y * uvScaleY + uvCenterY); uiVertex = UIUtility.PackageUIVertex(position, uv0, image.color); vh.AddVert(uiVertex); for (int i = 1; i < verticeCount; i++) { var cosA = Mathf.Cos(degree); var sinA = Mathf.Sin(degree); degree = reversed ? degree + degreeDelta : degree - degreeDelta; position = new Vector2(cosA * outerArea.x, sinA * outerArea.y); uv0 = new Vector2(position.x * uvScaleX + uvCenterX, position.y * uvScaleY + uvCenterY); uiVertex = UIUtility.PackageUIVertex(position, uv0, image.color); vh.AddVert(uiVertex); outterVertices.Add(position); } triangleCount = realSegements * 3; for (int i = 0, vIdx = 1; i < triangleCount - 3; i += 3, vIdx++) { vh.AddTriangle(vIdx, 0, vIdx + 1); } if (fillPercent == 1) { vh.AddTriangle(verticeCount - 1, 0, 1); } } else { verticeCount = realSegements * 2; for (int i = 0; i < verticeCount; i += 2) { var cosA = Mathf.Cos(degree); var sinA = Mathf.Sin(degree); degree = reversed ? degree + degreeDelta : degree - degreeDelta; position = new Vector3(cosA * innerArea.x, sinA * innerArea.y); uv0 = new Vector2(position.x * uvScaleX + uvCenterX, position.y * uvScaleY + uvCenterY); uiVertex = UIUtility.PackageUIVertex(position, uv0, image.color); vh.AddVert(uiVertex); innerVertices.Add(position); position = new Vector3(cosA * outerArea.x, sinA * outerArea.y); uv0 = new Vector2(position.x * uvScaleX + uvCenterX, position.y * uvScaleY + uvCenterY); uiVertex = UIUtility.PackageUIVertex(position, uv0, image.color); vh.AddVert(uiVertex); outterVertices.Add(position); } triangleCount = realSegements * 3 * 2; for (int i = 0, vIdx = 0; i < triangleCount - 6; i += 6, vIdx += 2) { vh.AddTriangle(vIdx + 1, vIdx, vIdx + 3); vh.AddTriangle(vIdx, vIdx + 2, vIdx + 3); } if (fillPercent == 1) { vh.AddTriangle(verticeCount - 1, verticeCount - 2, 1); vh.AddTriangle(verticeCount - 2, 0, 1); } } } public virtual bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera) { var sprite = image.overrideSprite; if (sprite == null) { return true; } Vector2 local; RectTransformUtility.ScreenPointToLocalPointInRectangle(image.rectTransform, screenPoint, eventCamera, out local); return Contains(local, outterVertices, innerVertices); } private bool Contains(Vector2 p, List outterVertices, List innerVertices) { var crossNumber = 0; crossNumber += UIUtility.RayCrossingCount(p, innerVertices); crossNumber += UIUtility.RayCrossingCount(p, outterVertices); return (crossNumber & 1) == 1; } public enum Align { Top = 1, Left = 2, Bottom = 3, Right = 4, } }