From 8c7210e33f271326a587709acccb0dfe3d251a87 Mon Sep 17 00:00:00 2001 From: Lncvrt Date: Sat, 5 Jul 2025 11:32:10 -0700 Subject: [PATCH] Add `ColorPickerUI` script --- Assets/Scripts/ColorPickerUI.cs | 193 +++++++++++++++++++++++++++ Assets/Scripts/ColorPickerUI.cs.meta | 2 + 2 files changed, 195 insertions(+) create mode 100644 Assets/Scripts/ColorPickerUI.cs create mode 100644 Assets/Scripts/ColorPickerUI.cs.meta diff --git a/Assets/Scripts/ColorPickerUI.cs b/Assets/Scripts/ColorPickerUI.cs new file mode 100644 index 0000000..bc25cdc --- /dev/null +++ b/Assets/Scripts/ColorPickerUI.cs @@ -0,0 +1,193 @@ +using UnityEngine; +using UnityEngine.UI; +using UnityEngine.InputSystem; +using System; + +public class ColorPickerUI : MonoBehaviour +{ + public RawImage hueImage; + public RawImage svImage; + public RectTransform svCursor; + public RectTransform hueCursor; + public event Action OnColorChanged; + + private Texture2D hueTexture; + private Texture2D svTexture; + + private const int hueHeight = 256; + private const int svSize = 256; + + private float selectedHue = 0f; + private float selectedS = 1f; + private float selectedV = 1f; + + private enum DragTarget { None, Hue, SV } + private DragTarget currentDrag = DragTarget.None; + + private Color lastColor; + + void Start() + { + GenerateHueBar(); + GenerateSVBox(selectedHue); + UpdateSVCursor(); + UpdateHueCursor(); + lastColor = GetSelectedColor(); + OnColorChanged?.Invoke(lastColor); + } + + void Update() + { + if (Pointer.current == null) + return; + + bool pressed = Pointer.current.press.isPressed; + Vector2 mousePos = Pointer.current.position.ReadValue(); + + if (pressed && currentDrag == DragTarget.None) + { + if (RectTransformUtility.RectangleContainsScreenPoint(hueImage.rectTransform, mousePos, null)) + currentDrag = DragTarget.Hue; + else if (RectTransformUtility.RectangleContainsScreenPoint(svImage.rectTransform, mousePos, null)) + currentDrag = DragTarget.SV; + } + + if (!pressed) + { + currentDrag = DragTarget.None; + return; + } + + bool colorChanged = false; + + if (currentDrag == DragTarget.Hue) + { + RectTransformUtility.ScreenPointToLocalPointInRectangle(hueImage.rectTransform, mousePos, null, out Vector2 localPoint); + float y = Mathf.Clamp01((localPoint.y + hueImage.rectTransform.rect.height / 2f) / hueImage.rectTransform.rect.height); + float newHue = 1f - y; + if (!Mathf.Approximately(newHue, selectedHue)) + { + selectedHue = newHue; + GenerateSVBox(selectedHue); + UpdateSVCursor(); + UpdateHueCursor(); + colorChanged = true; + } + } + else if (currentDrag == DragTarget.SV) + { + RectTransformUtility.ScreenPointToLocalPointInRectangle(svImage.rectTransform, mousePos, null, out Vector2 localPoint); + float rectWidth = svImage.rectTransform.rect.width; + float rectHeight = svImage.rectTransform.rect.height; + float x = (localPoint.x + rectWidth / 2f) / rectWidth; + float y = (localPoint.y + rectHeight / 2f) / rectHeight; + float newS = Mathf.Clamp01(x); + float newV = Mathf.Clamp01(y); + if (!Mathf.Approximately(newS, selectedS) || !Mathf.Approximately(newV, selectedV)) + { + selectedS = newS; + selectedV = newV; + UpdateSVCursor(); + colorChanged = true; + } + } + + if (colorChanged) + { + Color currentColor = GetSelectedColor(); + if (currentColor != lastColor) + { + lastColor = currentColor; + OnColorChanged?.Invoke(currentColor); + } + } + } + + void UpdateSVCursor() + { + if (svCursor == null) return; + Rect rect = svImage.rectTransform.rect; + float x = selectedS * rect.width; + float y = selectedV * rect.height; + x = Mathf.Clamp(x, 0, rect.width); + y = Mathf.Clamp(y, 0, rect.height); + Vector2 clampedPos = new Vector2(x - rect.width / 2f, y - rect.height / 2f); + svCursor.anchoredPosition = clampedPos; + } + + void UpdateHueCursor() + { + if (hueCursor == null) return; + Rect rect = hueImage.rectTransform.rect; + float y = (1f - selectedHue) * rect.height; + y = Mathf.Clamp(y, 0, rect.height); + Vector2 pos = new Vector2(0, y - rect.height / 2f); + hueCursor.anchoredPosition = pos; + } + + void GenerateHueBar() + { + hueTexture = new Texture2D(1, hueHeight); + hueTexture.wrapMode = TextureWrapMode.Clamp; + for (int y = 0; y < hueHeight; y++) + { + float h = 1f - (float)y / (hueHeight - 1); + Color color = Color.HSVToRGB(h, 1f, 1f); + hueTexture.SetPixel(0, y, color); + } + hueTexture.Apply(); + hueImage.texture = hueTexture; + } + + void GenerateSVBox(float hue) + { + svTexture = new Texture2D(svSize, svSize); + svTexture.wrapMode = TextureWrapMode.Clamp; + for (int y = 0; y < svSize; y++) + { + float v = (float)y / (svSize - 1); + for (int x = 0; x < svSize; x++) + { + float s = (float)x / (svSize - 1); + Color color = Color.HSVToRGB(hue, s, v); + svTexture.SetPixel(x, y, color); + } + } + svTexture.Apply(); + svImage.texture = svTexture; + } + + public Color GetSelectedColor() + { + return Color.HSVToRGB(selectedHue, selectedS, selectedV); + } + + public void SetSelectedColor(float r, float g, float b) + { + Color32 color = new((byte)r, (byte)g, (byte)b, 255); + + if (color.r == 0 && color.g == 0 && color.b == 0) + { + selectedS = 0; + selectedV = 0; + } + else + { + Color.RGBToHSV(color, out float h, out float s, out float v); + selectedHue = h; + selectedS = s; + selectedV = v; + } + + GenerateSVBox(selectedHue); + UpdateSVCursor(); + UpdateHueCursor(); + + var current = GetSelectedColor(); + if (current != lastColor) + { + lastColor = current; + OnColorChanged?.Invoke(current); + } + } +} \ No newline at end of file diff --git a/Assets/Scripts/ColorPickerUI.cs.meta b/Assets/Scripts/ColorPickerUI.cs.meta new file mode 100644 index 0000000..70362fa --- /dev/null +++ b/Assets/Scripts/ColorPickerUI.cs.meta @@ -0,0 +1,2 @@ +fileFormatVersion: 2 +guid: f8436a018ec18f3269f65a2fd73ed10b \ No newline at end of file