Skip to content

Instantly share code, notes, and snippets.

@arif-pandu
Created May 22, 2024 04:41
Show Gist options
  • Save arif-pandu/cac574f953f364566bebcfe0a5fb546f to your computer and use it in GitHub Desktop.
Save arif-pandu/cac574f953f364566bebcfe0a5fb546f to your computer and use it in GitHub Desktop.
Procedural circle UI component on Unity
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