using System.Collections.Generic; using System.Linq; using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace UltraCombos { [ExecuteAlways] public class DroppingPaint : MonoBehaviour { [SerializeField] private float size = 1; [SerializeField, Min(8)] private int segments = 16; [SerializeField] private float smooth = 3f; [SerializeField] private Vector2 distanceMinMax = new Vector2(1, 2); [SerializeField] private bool isDrop = false; [SerializeField] private bool isClear = false; private List> paints = new List>(); private List> smoothedPaints = new List>(); private void Update() { if (isDrop) { isDrop = false; Drop(); } if (isClear) { isClear = false; Clear(); } var t = Time.deltaTime * smooth; for (int i = 0; i < paints.Count; i++) { var points = paints[i]; for (int j = 0; j < points.Count; j++) { smoothedPaints[i][j] = Vector2.Lerp(smoothedPaints[i][j], points[j], t); } } } public void Drop() { var r = Random.Range(size * 0.1f, size * 0.2f); var px = Random.Range(-size * 0.4f, +size * 0.4f); var py = Random.Range(-size * 0.4f, +size * 0.4f); var angle = Mathf.PI * 2 / segments; var circle = Enumerable.Range(0, segments).Select(i => { var x = px + r * Mathf.Cos(angle * i); var y = py + r * Mathf.Sin(angle * i); return new Vector2(x, y); }).ToList(); var center = new Vector2(px, py); var minDist = size / segments * distanceMinMax.x; var maxDist = size / segments * distanceMinMax.y; var index = 0; foreach (var points in paints) { var pre = points.Select(p => ApplyPaintDrop(p, center, r)).ToArray(); var incs = pre.Select((p, i) => { var dist = (p - pre[(i + 1) % pre.Length]).magnitude; return dist < minDist ? 0 : Mathf.CeilToInt(dist / minDist); }).ToArray(); var newPoints = new List(); for (int i = 0; i < points.Count; i++) { var inc = incs[i]; if (inc == 0) { newPoints.Add(points[i]); } else { var p1 = points[i]; var p2 = points[(i + 1) % points.Count]; for (int k = 0; k < inc; k++) { var p = Vector2.Lerp(p1, p2, (float)k / inc); newPoints.Add(p); } } } points.Clear(); points.AddRange(newPoints); smoothedPaints[index].Clear(); smoothedPaints[index].AddRange(newPoints); for (int i = 0; i < points.Count; i++) { points[i] = ApplyPaintDrop(points[i], center, r); } index++; } paints.Add(circle); smoothedPaints.Add(circle.Select(c => center).ToList()); } public void Clear() { paints.Clear(); smoothedPaints.Clear(); } private void OnDrawGizmos() { #if UNITY_EDITOR using var mtx = new Handles.DrawingScope(transform.localToWorldMatrix); Handles.DrawWireCube(Vector3.zero, Vector2.one * size); if (smoothedPaints.Count == 0) return; var dh = 1f / smoothedPaints.Count; var h = 0f; var minDist = size / segments * distanceMinMax.x; var maxDist = size / segments * distanceMinMax.y; foreach (var points in smoothedPaints) { using var col = new Handles.DrawingScope(Color.HSVToRGB(h, 0.7f, 0.8f)); foreach (var point in points) { Handles.DrawWireDisc(new Vector3(point.x, point.y, 0), Vector3.forward, 0.02f); } //Handles.DrawPolyLine(points.Select(p => new Vector3(p.x, p.y, 0)).ToArray()); //Handles.DrawAAConvexPolygon(points.Select(p => new Vector3(p.x, p.y, 0)).ToArray()); for (int i = 0; i < points.Count; ++i) { var p1 = points[i]; var p2 = points[(i + 1) % points.Count]; var dist = (p1 - p2).magnitude; var t = Mathf.Clamp01((dist - minDist) / (maxDist - minDist)); var c = Color.Lerp(Color.cyan, Color.magenta, t); using var lc = new Handles.DrawingScope(c); Handles.DrawLine(p1, p2); } h += dh; } #endif } private Vector2 ApplyPaintDrop(Vector2 p, Vector2 c, float r) { var d = Vector2.Distance(p, c); if (d > r) { return c + (p - c) * Mathf.Sqrt(1 + (r * r) / (d * d)); } var edge = c + (p - c) * (r / d + 0.002f); return ApplyPaintDrop(edge, c, r); } private List ResamplePoints(List points, float minSpacing, float maxSpacing) { List newPoints = new List(); int count = points.Count; for (int i = 0; i < count; i++) { Vector2 p1 = points[i]; Vector2 p2 = points[(i + 1) % count]; float dist = Vector2.Distance(p1, p2); newPoints.Add(p1); if (dist > maxSpacing) { int numNewPoints = Mathf.FloorToInt(dist / minSpacing); for (int j = 1; j <= numNewPoints; j++) { float t = j / (float)(numNewPoints + 1); newPoints.Add(Vector2.Lerp(p1, p2, t)); } } } return newPoints; } } }