Created
March 24, 2025 19:34
-
-
Save hugohil/4923ba2a5e81995027c639377dc06aec to your computer and use it in GitHub Desktop.
Unity UI Polygon
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 System.Collections.Generic; | |
using UnityEngine; | |
using UnityEngine.UI; | |
[RequireComponent(typeof(CanvasRenderer))] | |
public class UIPolygon : Graphic { | |
[Header("UI Polygon")] | |
[SerializeField] private Vector2[] offsets = new Vector2[] { | |
new Vector2(-1f, 1f), | |
new Vector2(1f, 1f), | |
new Vector2(1f, -1f), | |
new Vector2(-1f, -1f) | |
}; | |
private List<Vector2> points = new List<Vector2>(); | |
[SerializeField] private Vector2 padding = new Vector2(0f, 0f); | |
[SerializeField] private bool fillCenter = true; | |
[Header("Border")] | |
[SerializeField] private bool hasBorder = true; | |
[SerializeField] private float borderWidth = 5f; | |
[SerializeField] private Color borderColor = Color.black; | |
[Header("Shadow")] | |
[SerializeField] private bool hasShadow = true; | |
[SerializeField] private Color shadowColor = new Color(0f, 0f, 0f, 0.5f); | |
[SerializeField] private Vector2 shadowOffset = new Vector2(10f, -10f); | |
private void Start() { | |
UpdatePoints(); | |
} | |
private Rect lastRect; | |
private Vector2 lastPadding; | |
private void Update() { | |
Rect currentRect = rectTransform.rect; | |
if (currentRect != lastRect || padding != lastPadding) { | |
UpdatePoints(); | |
lastRect = currentRect; | |
lastPadding = padding; | |
} | |
} | |
#if UNITY_EDITOR | |
private void OnValidate() { | |
UpdatePoints(); | |
} | |
#endif | |
private void UpdatePoints() { | |
points.Clear(); | |
Rect rect = rectTransform.rect; | |
for (int i = 0; i < offsets.Length; i++) { | |
points.Add(((rect.size * 0.5f) + padding) * offsets[i]); | |
} | |
SetAllDirty(); | |
} | |
protected override void OnPopulateMesh(VertexHelper vh) { | |
if (points == null || points.Count < 3) { | |
return; | |
} | |
vh.Clear(); | |
int vertexCount = 0; | |
if (hasShadow) { | |
vh.AddVert(shadowOffset, shadowColor, Vector2.zero); | |
vertexCount++; | |
for (int i = 0; i < points.Count; i++) { | |
vh.AddVert(points[i] + shadowOffset, shadowColor, Vector2.zero); | |
vertexCount++; | |
} | |
for (int i = 0; i < points.Count; i++) { | |
vh.AddTriangle(0, i + 1, ((i + 1) % points.Count) + 1); | |
} | |
} | |
if (fillCenter) { | |
vh.AddVert(Vector2.zero, color, Vector2.zero); | |
vertexCount++; | |
for (int i = 0; i < points.Count; i++) { | |
vh.AddVert(points[i], color, Vector2.zero); | |
} | |
for (int i = 0; i < points.Count; i++) { | |
int idx1 = i + vertexCount; | |
int idx2 = ((i + 1) % points.Count) + vertexCount; | |
vh.AddTriangle(vertexCount, idx1, idx2); | |
} | |
vertexCount += points.Count; | |
} | |
if (hasBorder) { | |
for (int i = 0; i < points.Count; i++) { | |
Vector2 p1 = points[i]; | |
Vector2 p2 = points[(i + 1) % points.Count]; | |
Vector2 edge = p2 - p1; | |
Vector2 edgePerp = Vector2.Perpendicular(edge.normalized); | |
Vector2 offset = edgePerp * borderWidth; | |
UIVertex v1 = new UIVertex(); | |
v1.position = p1 + offset; | |
v1.color = borderColor; | |
v1.uv0 = Vector2.zero; | |
UIVertex v2 = new UIVertex(); | |
v2.position = p2 + offset; | |
v2.color = borderColor; | |
v2.uv0 = Vector2.zero; | |
UIVertex v3 = new UIVertex(); | |
v3.position = p2 - offset; | |
v3.color = borderColor; | |
v3.uv0 = Vector2.zero; | |
UIVertex v4 = new UIVertex(); | |
v4.position = p1 - offset; | |
v4.color = borderColor; | |
v4.uv0 = Vector2.zero; | |
vh.AddVert(v1); | |
vh.AddVert(v2); | |
vh.AddVert(v3); | |
vh.AddVert(v4); | |
int idx = vertexCount; | |
vh.AddTriangle(idx, idx + 1, idx + 2); | |
vh.AddTriangle(idx, idx + 2, idx + 3); | |
vertexCount += 4; | |
} | |
for (int i = 0; i < points.Count; i++) { | |
Vector2 current = points[i]; | |
Vector2 prev = points[(i - 1 + points.Count) % points.Count]; | |
Vector2 next = points[(i + 1) % points.Count]; | |
Vector2 toPrev = (prev - current).normalized; | |
Vector2 toNext = (next - current).normalized; | |
Vector2 prevPerp = Vector2.Perpendicular(toPrev); | |
Vector2 nextPerp = Vector2.Perpendicular(toNext); | |
// Check if this is an inner corner (concave) or outer corner (convex) | |
float crossProduct = (toPrev.x * toNext.y) - (toPrev.y * toNext.x); | |
Vector2 prevOffset = prevPerp * borderWidth * Mathf.Sign(crossProduct); | |
Vector2 prevCorner = current - prevOffset; | |
Vector2 nextOffset = nextPerp * borderWidth * Mathf.Sign(crossProduct); | |
Vector2 nextCorner = current + nextOffset; | |
float segmentAngle = Vector2.Angle(toPrev, toNext) * Mathf.Deg2Rad; | |
float miterLength = borderWidth / Mathf.Sin(segmentAngle / 2); | |
Vector2 miterDir = (nextPerp - prevPerp).normalized * Mathf.Sign(crossProduct); | |
Vector2 miterOffset = (miterDir * miterLength); | |
Vector2 miterCorner = current + miterOffset; | |
UIVertex v1 = new UIVertex(); | |
v1.position = current; | |
v1.color = borderColor; | |
v1.uv0 = Vector2.zero; | |
UIVertex v2 = new UIVertex(); | |
v2.position = miterCorner; | |
v2.color = borderColor; | |
v2.uv0 = Vector2.zero; | |
UIVertex v3 = new UIVertex(); | |
v3.position = prevCorner; | |
v3.color = borderColor; | |
v3.uv0 = Vector2.zero; | |
UIVertex v4 = new UIVertex(); | |
v4.position = nextCorner; | |
v4.color = borderColor; | |
v4.uv0 = Vector2.zero; | |
vh.AddVert(v1); | |
vh.AddVert(v2); | |
vh.AddVert(v3); | |
vh.AddVert(v4); | |
int idx = vertexCount; | |
vh.AddTriangle(idx, idx + 1, idx + 2); | |
vh.AddTriangle(idx, idx + 1, idx + 3); | |
vertexCount += 4; | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment