using System; using UnityEngine; using UnityEngine.UI; /// /// Compact runtime HSV colour picker: a saturation/value square, a hue bar and a live /// swatch. Builds its own UI as children when is called and raises /// on user interaction. The square is a pure-hue base with two /// static gradient overlays on top (white-out -> saturation, black-out -> value), so /// only the base tint changes per hue - no per-frame texture rebuilds. Hue is never /// lost while dragging the square (the square only edits S/V, the bar only edits H). /// public class HsvColorPicker : MonoBehaviour { /// Raised with the new colour when the user moves the square or hue bar. public event Action Changed; private float _h, _s, _v, _a = 1f; private Image _hueBase; // pure-hue background of the SV square private RectTransform _svHandle; private RectTransform _hueHandle; private Image _swatch; /// Current colour (HSV -> RGB, with the preserved alpha). public Color Value { get { Color c = Color.HSVToRGB(_h, _s, _v); c.a = _a; return c; } } /// Shows without raising (external set). public void SetColor(Color c) { Color.RGBToHSV(c, out _h, out _s, out _v); _a = c.a; Refresh(); } /// Builds the picker (square + hue bar + swatch) under this object, top-left aligned. public void Build(float squareSize, float hueHeight) { // Saturation / value square: base hue + two gradient overlays, top-left of this rect. var sv = PaintHud.NewRect("SV", transform); TopLeft(sv, new Vector2(0f, 0f), new Vector2(squareSize, squareSize)); _hueBase = PaintHud.AddImage(sv.gameObject, Color.red); // raycast target for the drag relay PaintHud.AddOverlay(sv, PaintHud.SaturationSprite()); PaintHud.AddOverlay(sv, PaintHud.ValueSprite()); sv.gameObject.AddComponent().Picked += OnSvPicked; _svHandle = PaintHud.NewRect("Handle", sv); _svHandle.sizeDelta = new Vector2(12f, 12f); var svHandleImg = PaintHud.AddImage(_svHandle.gameObject, Color.white); svHandleImg.raycastTarget = false; // Hue bar below the square. var hue = PaintHud.NewRect("Hue", transform); TopLeft(hue, new Vector2(0f, -(squareSize + 6f)), new Vector2(squareSize, hueHeight)); var hueImg = hue.gameObject.AddComponent(); hueImg.sprite = PaintHud.HueSprite(); hueImg.type = Image.Type.Simple; hue.gameObject.AddComponent().Picked += OnHuePicked; _hueHandle = PaintHud.NewRect("Handle", hue); _hueHandle.sizeDelta = new Vector2(4f, hueHeight + 4f); var hueHandleImg = PaintHud.AddImage(_hueHandle.gameObject, Color.white); hueHandleImg.raycastTarget = false; // Swatch, to the right of the square. var sw = PaintHud.NewRect("Swatch", transform); TopLeft(sw, new Vector2(squareSize + 10f, 0f), new Vector2(34f, 34f)); _swatch = PaintHud.AddImage(sw.gameObject, Color.white); _swatch.raycastTarget = false; Refresh(); } private void OnSvPicked(Vector2 p) { _s = p.x; _v = p.y; Refresh(); Changed?.Invoke(Value); } private void OnHuePicked(Vector2 p) { _h = p.x; Refresh(); Changed?.Invoke(Value); } private void Refresh() { if (_hueBase) _hueBase.color = Color.HSVToRGB(_h, 1f, 1f); if (_svHandle) { _svHandle.anchorMin = _svHandle.anchorMax = new Vector2(_s, _v); _svHandle.anchoredPosition = Vector2.zero; } if (_hueHandle) { _hueHandle.anchorMin = _hueHandle.anchorMax = new Vector2(_h, 0.5f); _hueHandle.anchoredPosition = Vector2.zero; } if (_swatch) _swatch.color = Color.HSVToRGB(_h, _s, _v); } /// Anchors a child to this rect's top-left corner with the given offset and size. private static void TopLeft(RectTransform rt, Vector2 offset, Vector2 size) { rt.anchorMin = rt.anchorMax = new Vector2(0f, 1f); rt.pivot = new Vector2(0f, 1f); rt.anchoredPosition = offset; rt.sizeDelta = size; } }