Created
May 22, 2024 04:41
-
-
Save arif-pandu/cac574f953f364566bebcfe0a5fb546f to your computer and use it in GitHub Desktop.
Procedural circle UI component on Unity
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using UnityEngine.UI; | |
#if UNITY_EDITOR | |
using UnityEditor; | |
// Custom Editor to order the variables in the Inspector similar to Image component | |
[CustomEditor(typeof(UICircleRenderer)), CanEditMultipleObjects] | |
public class CircleGraphicEditor : Editor | |
{ | |
private SerializedProperty colorProp; | |
private SerializedProperty fillPercentageProp; | |
private void OnEnable() | |
{ | |
colorProp = serializedObject.FindProperty("m_Color"); | |
fillPercentageProp = serializedObject.FindProperty("fillPercentage"); | |
} | |
public override void OnInspectorGUI() | |
{ | |
serializedObject.Update(); | |
EditorGUILayout.PropertyField(colorProp); | |
DrawPropertiesExcluding(serializedObject, "m_Script", "m_Color", "m_OnCullStateChanged", "fillPercentage"); | |
// Show fill percentage property only if the mode is CircularFill or CircularEdge | |
UICircleRenderer circleGraphic = (UICircleRenderer)target; | |
if (circleGraphic.mode == UICircleRenderer.Mode.CircularFill || circleGraphic.mode == UICircleRenderer.Mode.CircularEdge) | |
{ | |
EditorGUILayout.PropertyField(fillPercentageProp); | |
} | |
serializedObject.ApplyModifiedProperties(); | |
} | |
} | |
#endif | |
[RequireComponent(typeof(CanvasRenderer))] | |
[AddComponentMenu("UI/Circle Graphic", 12)] | |
public class UICircleRenderer : MaskableGraphic | |
{ | |
public enum Mode { FillInside = 0, FillOutside = 1, Edge = 2, CircularFill = 3, CircularEdge = 4 }; | |
#pragma warning disable 0649 | |
[SerializeField] | |
private int detail = 64; | |
[SerializeField] | |
public Mode mode; | |
[SerializeField] | |
[Tooltip("Edge mode only")] | |
private float edgeThickness = 1; | |
[SerializeField] | |
[Range(0f, 1f)] | |
private float fillPercentage = 1f; | |
#pragma warning restore 0649 | |
private Vector2 uv = Vector2.zero; | |
private Color32 color32; | |
private float width = 1f, height = 1f; | |
private float deltaRadians; | |
protected override void OnPopulateMesh(VertexHelper vh) | |
{ | |
Rect r = GetPixelAdjustedRect(); | |
color32 = color; | |
width = r.width * 0.5f; | |
height = r.height * 0.5f; | |
vh.Clear(); | |
deltaRadians = 360f / detail * Mathf.Deg2Rad; | |
switch (mode) | |
{ | |
case Mode.FillInside: | |
FillInside(vh); | |
break; | |
case Mode.FillOutside: | |
FillOutside(vh); | |
break; | |
case Mode.Edge: | |
GenerateEdges(vh); | |
break; | |
case Mode.CircularFill: | |
CircularFill(vh, fillPercentage); | |
break; | |
case Mode.CircularEdge: | |
CircularEdge(vh, fillPercentage); | |
break; | |
} | |
} | |
private void FillInside(VertexHelper vh) | |
{ | |
vh.AddVert(new Vector3(0f, 0f, 0f), color32, uv); | |
vh.AddVert(new Vector3(width, 0f, 0f), color32, uv); | |
int triangleIndex = 2; | |
for (int i = 1; i < detail; i++, triangleIndex++) | |
{ | |
float radians = i * deltaRadians; | |
vh.AddVert(new Vector3(Mathf.Cos(radians) * width, Mathf.Sin(radians) * height, 0f), color32, uv); | |
vh.AddTriangle(triangleIndex, triangleIndex - 1, 0); | |
} | |
vh.AddTriangle(1, triangleIndex - 1, 0); | |
} | |
private void FillOutside(VertexHelper vh) | |
{ | |
int quarterDetail = (detail + 3) / 4; | |
deltaRadians = 360f / (quarterDetail * 4) * Mathf.Deg2Rad; | |
vh.AddVert(new Vector3(width, height, 0f), color32, uv); | |
vh.AddVert(new Vector3(-width, height, 0f), color32, uv); | |
vh.AddVert(new Vector3(-width, -height, 0f), color32, uv); | |
vh.AddVert(new Vector3(width, -height, 0f), color32, uv); | |
int triangleIndex = 4; | |
FillOutsideQuarter(vh, new Vector3(width, 0f, 0f), 0, quarterDetail, ref triangleIndex); | |
FillOutsideQuarter(vh, new Vector3(0f, height, 0f), 1, quarterDetail, ref triangleIndex); | |
FillOutsideQuarter(vh, new Vector3(-width, 0f, 0f), 2, quarterDetail, ref triangleIndex); | |
FillOutsideQuarter(vh, new Vector3(0f, -height, 0f), 3, quarterDetail, ref triangleIndex); | |
} | |
private void FillOutsideQuarter(VertexHelper vh, Vector3 initialPoint, int quarterIndex, int detail, ref int triangleIndex) | |
{ | |
int startIndex = quarterIndex * detail; | |
int endIndex = (quarterIndex + 1) * detail; | |
vh.AddVert(initialPoint, color32, uv); | |
triangleIndex++; | |
for (int i = startIndex + 1; i <= endIndex; i++, triangleIndex++) | |
{ | |
float radians = i * deltaRadians; | |
vh.AddVert(new Vector3(Mathf.Cos(radians) * width, Mathf.Sin(radians) * height, 0f), color32, uv); | |
vh.AddTriangle(quarterIndex, triangleIndex - 1, triangleIndex); | |
} | |
} | |
private void GenerateEdges(VertexHelper vh) | |
{ | |
float innerWidth = width - edgeThickness; | |
float innerHeight = height - edgeThickness; | |
vh.AddVert(new Vector3(width, 0f, 0f), color32, uv); | |
vh.AddVert(new Vector3(innerWidth, 0f, 0f), color32, uv); | |
int triangleIndex = 2; | |
for (int i = 1; i < detail; i++, triangleIndex += 2) | |
{ | |
float radians = i * deltaRadians; | |
float cos = Mathf.Cos(radians); | |
float sin = Mathf.Sin(radians); | |
vh.AddVert(new Vector3(cos * width, sin * height, 0f), color32, uv); | |
vh.AddVert(new Vector3(cos * innerWidth, sin * innerHeight, 0f), color32, uv); | |
vh.AddTriangle(triangleIndex, triangleIndex - 2, triangleIndex - 1); | |
vh.AddTriangle(triangleIndex, triangleIndex - 1, triangleIndex + 1); | |
} | |
vh.AddTriangle(0, triangleIndex - 2, triangleIndex - 1); | |
vh.AddTriangle(0, triangleIndex - 1, 1); | |
} | |
private void CircularFill(VertexHelper vh, float fillAmount) | |
{ | |
int fillDetail = Mathf.CeilToInt(detail * fillAmount); | |
vh.AddVert(new Vector3(0f, 0f, 0f), color32, uv); | |
int triangleIndex = 1; | |
for (int i = 0; i <= fillDetail; i++, triangleIndex++) | |
{ | |
float radians = i * deltaRadians; | |
vh.AddVert(new Vector3(Mathf.Cos(radians) * width, Mathf.Sin(radians) * height, 0f), color32, uv); | |
if (i > 0) | |
{ | |
vh.AddTriangle(0, triangleIndex - 1, triangleIndex); | |
} | |
} | |
} | |
private void CircularEdge(VertexHelper vh, float fillAmount) | |
{ | |
int fillDetail = Mathf.CeilToInt(detail * fillAmount); | |
float innerWidth = width - edgeThickness; | |
float innerHeight = height - edgeThickness; | |
vh.AddVert(new Vector3(width, 0f, 0f), color32, uv); | |
vh.AddVert(new Vector3(innerWidth, 0f, 0f), color32, uv); | |
int triangleIndex = 2; | |
for (int i = 1; i <= fillDetail; i++, triangleIndex += 2) | |
{ | |
float radians = i * deltaRadians; | |
float cos = Mathf.Cos(radians); | |
float sin = Mathf.Sin(radians); | |
vh.AddVert(new Vector3(cos * width, sin * height, 0f), color32, uv); | |
vh.AddVert(new Vector3(cos * innerWidth, sin * innerHeight, 0f), color32, uv); | |
vh.AddTriangle(triangleIndex, triangleIndex - 2, triangleIndex - 1); | |
vh.AddTriangle(triangleIndex, triangleIndex - 1, triangleIndex + 1); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment