Persist material scalars, reset the HUD, and split alpha from the picker
Save and restore the emission intensity and the three camo knobs (half-Lambert, half-Lambert brightness, specular dampen) with a named painting. The .paint format moves to v3 with a small scalar block between the header and the pixel maps; v2 paintings still load with their painter values left untouched. Loading re-syncs the emission and camo sliders, and peers pick the values up for free through the existing owner-authoritative paint NetworkVariables. Add a HUD reset (Lib_ResetHud button plus context-menu fallback) that behaves as if the saved hud-layout file were deleted: it removes the file, restores panels to their authored prefab positions, and resets the brush values to the painter/material defaults captured before any carry/restore runs. Remove the alpha bar from HsvColorPicker so alpha is driven by standalone Slider_AlbedoAlpha / Slider_EmissionAlpha bindings; the picker now writes RGB only and the sliders write alpha only, with no feedback between them. Add a generic SliderCurve component that reshapes a slider's handle position into a non-linear value (with an inverse for setting values back), and make the slider wiring curve-aware. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
fe8f12b8bb
commit
057c99fa1d
7 files changed
+313
-128
No files matched your search
@@ -3,35 +3,33 @@ using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Compact runtime HSV colour picker: a saturation/value square, a hue bar, an alpha bar and a
|
||||
/// live swatch. 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, the alpha bar only edits A).
|
||||
/// Compact runtime HSV colour picker: a saturation/value square, a hue bar and a live swatch. 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). Alpha is
|
||||
/// edited separately by a standalone slider in the HUD, not by this picker; <see cref="SetColor"/>
|
||||
/// still stores the incoming alpha so <see cref="Value"/> round-trips it.
|
||||
///
|
||||
/// <see cref="Build"/> creates the child structure (stable names, no sprites or hooks); it can
|
||||
/// be baked into a prefab. <see cref="Rebind"/> then (re)assigns the runtime gradient sprites
|
||||
/// (which a prefab cannot store) and (re)hooks the drag relays - it runs for both the code-built
|
||||
/// picker and one instantiated from a prefab, so the two are behaviourally identical. For a prefab
|
||||
/// baked before the alpha bar existed it also builds that bar on the fly (and grows the picker's
|
||||
/// reserved layout height), so no re-bake is required.
|
||||
/// picker and one instantiated from a prefab, so the two are behaviourally identical. A leftover
|
||||
/// alpha bar from a pre-split prefab is hidden by <see cref="Rebind"/>, so no re-bake is required.
|
||||
/// </summary>
|
||||
public class HsvColorPicker : MonoBehaviour
|
||||
{
|
||||
/// <summary>Raised with the new colour when the user moves the square, hue bar or alpha bar.</summary>
|
||||
/// <summary>Raised with the new colour (RGB; alpha is preserved, not edited here) when the user moves
|
||||
/// the square or hue bar.</summary>
|
||||
public event Action<Color> Changed;
|
||||
|
||||
private float _h, _s, _v, _a = 1f;
|
||||
|
||||
// Gap between the stacked square / hue / alpha rows (matches the layout in Build).
|
||||
// Gap between the stacked square / hue rows (matches the layout in Build).
|
||||
private const float RowGap = 6f;
|
||||
private static readonly Color AlphaBackdrop = new Color(0.5f, 0.5f, 0.5f, 1f);
|
||||
|
||||
private Image _hueBase; // pure-hue background of the SV square
|
||||
private RectTransform _svHandle;
|
||||
private RectTransform _hueHandle;
|
||||
private Image _alphaGrad; // colour-tinted transparency ramp of the alpha bar
|
||||
private RectTransform _alphaHandle;
|
||||
private Image _swatch;
|
||||
|
||||
/// <summary>Current colour (HSV -> RGB, with the preserved alpha).</summary>
|
||||
@@ -54,7 +52,7 @@ public class HsvColorPicker : MonoBehaviour
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the picker structure (square + hue bar + alpha bar + swatch) under this object,
|
||||
/// Builds the picker structure (square + hue bar + swatch) under this object,
|
||||
/// top-left aligned. Layout only - no gradient sprites and no event hooks, so the result is
|
||||
/// safe to bake into a prefab. Call <see cref="Rebind"/> afterwards (at runtime) to make it
|
||||
/// functional.
|
||||
@@ -87,41 +85,12 @@ public class HsvColorPicker : MonoBehaviour
|
||||
hueHandle.sizeDelta = new Vector2(4f, hueHeight + 4f);
|
||||
PaintHud.AddImage(hueHandle.gameObject, Color.white).raycastTarget = false;
|
||||
|
||||
// Alpha bar below the hue bar.
|
||||
BuildAlphaBar(sv, hue);
|
||||
|
||||
// Swatch, to the right of the square.
|
||||
var sw = PaintHud.NewRect("Swatch", transform);
|
||||
TopLeft(sw, new Vector2(squareSize + 10f, 0f), new Vector2(34f, 34f));
|
||||
PaintHud.AddImage(sw.gameObject, Color.white).raycastTarget = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the alpha bar (grey backdrop + colour-tinted transparency ramp + drag relay + handle)
|
||||
/// directly below the hue bar, sized/positioned from the existing SV square and hue bar so it
|
||||
/// works for both a fresh build and an older baked prefab. Sprites/hooks are applied by
|
||||
/// <see cref="Rebind"/>.
|
||||
/// </summary>
|
||||
private RectTransform BuildAlphaBar(RectTransform sv, RectTransform hue)
|
||||
{
|
||||
float width = sv.sizeDelta.x;
|
||||
float barHeight = hue.sizeDelta.y;
|
||||
float y = hue.anchoredPosition.y - (barHeight + RowGap);
|
||||
|
||||
var alpha = PaintHud.NewRect("Alpha", transform);
|
||||
TopLeft(alpha, new Vector2(0f, y), new Vector2(width, barHeight));
|
||||
PaintHud.AddImage(alpha.gameObject, AlphaBackdrop); // raycast target for the drag relay
|
||||
|
||||
PaintHud.AddOverlay(alpha, "AlphaGrad"); // tinted alpha ramp, sprite assigned in Rebind
|
||||
|
||||
alpha.gameObject.AddComponent<UiPointerSurface>();
|
||||
|
||||
var handle = PaintHud.NewRect("Handle", alpha);
|
||||
handle.sizeDelta = new Vector2(4f, barHeight + 4f);
|
||||
PaintHud.AddImage(handle.gameObject, Color.white).raycastTarget = false;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Re-finds the child widgets, (re)assigns the runtime gradient sprites and (re)hooks the drag
|
||||
/// relays. Idempotent (unsubscribe-then-subscribe), so it is safe whether the structure was just
|
||||
@@ -161,32 +130,10 @@ public class HsvColorPicker : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// Alpha bar: build it if the (older) prefab lacks it, then (re)assign sprite + relay.
|
||||
var alpha = transform.Find("Alpha") as RectTransform;
|
||||
bool builtAlpha = false;
|
||||
if (!alpha && sv is RectTransform svRect && hue)
|
||||
{
|
||||
alpha = BuildAlphaBar(svRect, hue);
|
||||
builtAlpha = true;
|
||||
}
|
||||
if (alpha)
|
||||
{
|
||||
var grad = alpha.Find("AlphaGrad");
|
||||
if (grad && grad.TryGetComponent(out Image gradImg))
|
||||
{
|
||||
gradImg.sprite = PaintHud.AlphaSprite();
|
||||
_alphaGrad = gradImg;
|
||||
}
|
||||
_alphaHandle = alpha.Find("Handle") as RectTransform;
|
||||
|
||||
if (alpha.TryGetComponent(out UiPointerSurface alphaPointer))
|
||||
{
|
||||
alphaPointer.Picked -= OnAlphaPicked;
|
||||
alphaPointer.Picked += OnAlphaPicked;
|
||||
}
|
||||
|
||||
if (builtAlpha) GrowReservedHeight(alpha.sizeDelta.y + RowGap);
|
||||
}
|
||||
// Alpha now lives on a standalone slider (PaintHud), not in the picker. Hide any alpha bar a
|
||||
// pre-split prefab still carries so it does not show as a dead, non-functional strip.
|
||||
var leftoverAlpha = transform.Find("Alpha");
|
||||
if (leftoverAlpha) leftoverAlpha.gameObject.SetActive(false);
|
||||
|
||||
var swatch = transform.Find("Swatch");
|
||||
if (swatch) _swatch = swatch.GetComponent<Image>();
|
||||
@@ -194,15 +141,6 @@ public class HsvColorPicker : MonoBehaviour
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>Grows this picker's LayoutElement so a runtime-added alpha bar does not overlap the
|
||||
/// widget below it; the panel's ContentSizeFitter then expands to match. No-op without a LayoutElement.</summary>
|
||||
private void GrowReservedHeight(float extra)
|
||||
{
|
||||
if (!TryGetComponent(out LayoutElement le)) return;
|
||||
if (le.minHeight > 0f) le.minHeight += extra;
|
||||
if (le.preferredHeight > 0f) le.preferredHeight += extra;
|
||||
}
|
||||
|
||||
private void OnSvPicked(Vector2 p)
|
||||
{
|
||||
_s = p.x;
|
||||
@@ -218,13 +156,6 @@ public class HsvColorPicker : MonoBehaviour
|
||||
Changed?.Invoke(Value);
|
||||
}
|
||||
|
||||
private void OnAlphaPicked(Vector2 p)
|
||||
{
|
||||
_a = p.x;
|
||||
Refresh();
|
||||
Changed?.Invoke(Value);
|
||||
}
|
||||
|
||||
private void Refresh()
|
||||
{
|
||||
if (_hueBase) _hueBase.color = Color.HSVToRGB(_h, 1f, 1f);
|
||||
@@ -238,12 +169,6 @@ public class HsvColorPicker : MonoBehaviour
|
||||
_hueHandle.anchorMin = _hueHandle.anchorMax = new Vector2(_h, 0.5f);
|
||||
_hueHandle.anchoredPosition = Vector2.zero;
|
||||
}
|
||||
if (_alphaGrad) _alphaGrad.color = Color.HSVToRGB(_h, _s, _v); // tint the transparency ramp to the current colour
|
||||
if (_alphaHandle)
|
||||
{
|
||||
_alphaHandle.anchorMin = _alphaHandle.anchorMax = new Vector2(_a, 0.5f);
|
||||
_alphaHandle.anchoredPosition = Vector2.zero;
|
||||
}
|
||||
if (_swatch) _swatch.color = Color.HSVToRGB(_h, _s, _v);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user