把圖片內切割成正多邊形/圓形(邊數多)/圓環
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Sprites;
/// <summary>
/// 把圖片內切割成正多邊形/圓形(邊數多)/圓環
/// </summary>
[AddComponentMenu("UI/Circle Image")]
public class CircleUIImage : BaseUIImage
{
[Header("多邊形填充比例")]
[Range(0, 1)]
public float fillPercent = 1f;
[Header("是否填充多邊形")]
public bool fill = true;
[Header("圓環寬度")]
public float thickness = 5;
[Header("構成圓形的多邊形邊數,默認爲30個邊")]
[Range(3, 100)]
public int segements = 30;
private List<Vector3> innerVertices;
private List<Vector3> outterVertices;
// Use this for initialization
void Awake()
{
innerVertices = new List<Vector3>();
outterVertices = new List<Vector3>();
}
// Update is called once per frame
void Update()
{
this.thickness = (float)Mathf.Clamp(this.thickness, 0, rectTransform.rect.width / 2);
}
protected override void OnPopulateMesh(VertexHelper vh)
{
vh.Clear();
innerVertices.Clear();
outterVertices.Clear();
float degreeDelta = (float)(2 * Mathf.PI / segements);
int curSegements = (int)(segements * fillPercent);
float tw = rectTransform.rect.width;
float th = rectTransform.rect.height;
float outerRadius = rectTransform.pivot.x * tw;
float innerRadius = rectTransform.pivot.x * tw - thickness;
Vector4 uv = overrideSprite != null ? DataUtility.GetOuterUV(overrideSprite) : Vector4.zero;
float uvCenterX = (uv.x + uv.z) * 0.5f;
float uvCenterY = (uv.y + uv.w) * 0.5f;
float uvScaleX = (uv.z - uv.x) / tw;
float uvScaleY = (uv.w - uv.y) / th;
float curDegree = 0;
UIVertex uiVertex;
int verticeCount;
int triangleCount;
Vector2 curVertice;
if (fill) //圓形
{
curVertice = Vector2.zero;
verticeCount = curSegements + 1;
uiVertex = new UIVertex();
uiVertex.color = color;
uiVertex.position = curVertice;
uiVertex.uv0 = new Vector2(curVertice.x * uvScaleX + uvCenterX, curVertice.y * uvScaleY + uvCenterY);
vh.AddVert(uiVertex);
for (int i = 1; i < verticeCount; i++)
{
float cosA = Mathf.Cos(curDegree);
float sinA = Mathf.Sin(curDegree);
curVertice = new Vector2(cosA * outerRadius, sinA * outerRadius);
curDegree += degreeDelta;
uiVertex = new UIVertex();
uiVertex.color = color;
uiVertex.position = curVertice;
uiVertex.uv0 = new Vector2(curVertice.x * uvScaleX + uvCenterX, curVertice.y * uvScaleY + uvCenterY);
vh.AddVert(uiVertex);
outterVertices.Add(curVertice);
}
triangleCount = curSegements * 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 = curSegements * 2;
for (int i = 0; i < verticeCount; i += 2)
{
float cosA = Mathf.Cos(curDegree);
float sinA = Mathf.Sin(curDegree);
curDegree += degreeDelta;
curVertice = new Vector3(cosA * innerRadius, sinA * innerRadius);
uiVertex = new UIVertex();
uiVertex.color = color;
uiVertex.position = curVertice;
uiVertex.uv0 = new Vector2(curVertice.x * uvScaleX + uvCenterX, curVertice.y * uvScaleY + uvCenterY);
vh.AddVert(uiVertex);
innerVertices.Add(curVertice);
curVertice = new Vector3(cosA * outerRadius, sinA * outerRadius);
uiVertex = new UIVertex();
uiVertex.color = color;
uiVertex.position = curVertice;
uiVertex.uv0 = new Vector2(curVertice.x * uvScaleX + uvCenterX, curVertice.y * uvScaleY + uvCenterY);
vh.AddVert(uiVertex);
outterVertices.Add(curVertice);
}
triangleCount = curSegements * 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 override bool IsRaycastLocationValid(Vector2 screenPoint, Camera eventCamera)
{
Sprite sprite = overrideSprite;
if (sprite == null)
return true;
Vector2 local;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rectTransform, screenPoint, eventCamera, out local);
return Contains(local, outterVertices, innerVertices);
}
private bool Contains(Vector2 p, List<Vector3> outterVertices, List<Vector3> innerVertices)
{
var crossNumber = 0;
RayCrossing(p, innerVertices, ref crossNumber);//檢測內環
RayCrossing(p, outterVertices, ref crossNumber);//檢測外環
return (crossNumber & 1) == 1;
}
/// <summary>
/// 使用RayCrossing算法判斷點擊點是否在封閉多邊形裏
/// </summary>
/// <param name="p"></param>
/// <param name="vertices"></param>
/// <param name="crossNumber"></param>
private void RayCrossing(Vector2 p, List<Vector3> vertices, ref int crossNumber)
{
for (int i = 0, count = vertices.Count; i < count; i++)
{
var v1 = vertices[i];
var v2 = vertices[(i + 1) % count];
//點擊點水平線必須與兩頂點線段相交
if (((v1.y <= p.y) && (v2.y > p.y))
|| ((v1.y > p.y) && (v2.y <= p.y)))
{
//只考慮點擊點右側方向,點擊點水平線與線段相交,且交點x > 點擊點x,則crossNumber+1
if (p.x < v1.x + (p.y - v1.y) / (v2.y - v1.y) * (v2.x - v1.x))
{
crossNumber += 1;
}
}
}
}
}