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
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
/// <summary>
|
||||
/// Reshapes a UI <see cref="Slider"/>'s response with an <see cref="AnimationCurve"/> so the value runs
|
||||
/// non-linearly between the slider's authored <c>minValue</c>/<c>maxValue</c>. The curve maps the handle
|
||||
/// position (x, 0..1 - linear in screen space) to a value fraction (y, 0..1) that is then scaled to the
|
||||
/// slider's range. The slider's own min/max stays the single source of truth for the range; this only
|
||||
/// bends the mapping inside it - so dropping the component onto an existing slider needs no range re-entry.
|
||||
///
|
||||
/// <see cref="Value"/> is the curved output, <see cref="SetOutput"/> positions the handle to show a given
|
||||
/// output (inverting the curve), and <see cref="ValueChanged"/> raises the curved value whenever the
|
||||
/// handle moves - so the component is usable both standalone (wire <see cref="ValueChanged"/>) and from
|
||||
/// code that drives the raw slider (read <see cref="Value"/>).
|
||||
///
|
||||
/// The curve should be monotonic increasing (the default linear is); the inverse used by
|
||||
/// <see cref="SetOutput"/> assumes that.
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(Slider))]
|
||||
[DisallowMultipleComponent]
|
||||
public class SliderCurve : MonoBehaviour
|
||||
{
|
||||
[Tooltip("Maps the handle position (x, 0..1) to a value fraction (y, 0..1), scaled to the slider's " +
|
||||
"minValue/maxValue. Default linear = no change. Should be monotonic increasing.")]
|
||||
public AnimationCurve response = AnimationCurve.Linear(0f, 0f, 1f, 1f);
|
||||
|
||||
/// <summary>Raised with the curved output whenever the handle moves.</summary>
|
||||
public event Action<float> ValueChanged;
|
||||
|
||||
private Slider _slider;
|
||||
private Slider Slider => _slider ? _slider : (_slider = GetComponent<Slider>());
|
||||
|
||||
/// <summary>The curved output value for the current handle position.</summary>
|
||||
public float Value => Slider ? Curve(Slider.normalizedValue) : 0f;
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (Slider) Slider.onValueChanged.AddListener(OnSliderChanged);
|
||||
}
|
||||
|
||||
private void OnDisable()
|
||||
{
|
||||
if (_slider) _slider.onValueChanged.RemoveListener(OnSliderChanged);
|
||||
}
|
||||
|
||||
private void OnSliderChanged(float _) => ValueChanged?.Invoke(Value);
|
||||
|
||||
/// <summary>Positions the handle so <see cref="Value"/> reads <paramref name="output"/>, raising the
|
||||
/// slider's <c>onValueChanged</c> (and thus <see cref="ValueChanged"/>).</summary>
|
||||
public void SetOutput(float output)
|
||||
{
|
||||
if (Slider) Slider.normalizedValue = Position(output); // normalizedValue setter notifies
|
||||
}
|
||||
|
||||
/// <summary>As <see cref="SetOutput"/> but without raising any change event (for silent seeding).</summary>
|
||||
public void SetOutputWithoutNotify(float output)
|
||||
{
|
||||
if (Slider) Slider.SetValueWithoutNotify(Mathf.Lerp(Slider.minValue, Slider.maxValue, Position(output)));
|
||||
}
|
||||
|
||||
// handle position (0..1) -> curved output value in [minValue, maxValue]
|
||||
private float Curve(float t)
|
||||
{
|
||||
float y = Mathf.Clamp01(response.Evaluate(Mathf.Clamp01(t)));
|
||||
float v = Mathf.Lerp(Slider.minValue, Slider.maxValue, y);
|
||||
return Slider && Slider.wholeNumbers ? Mathf.Round(v) : v;
|
||||
}
|
||||
|
||||
// curved output value -> handle position (0..1), inverting the (monotonic) curve via bisection
|
||||
private float Position(float output)
|
||||
{
|
||||
var s = Slider;
|
||||
if (!s) return 0f;
|
||||
float target = Mathf.Clamp01(Mathf.InverseLerp(s.minValue, s.maxValue, output)); // desired y
|
||||
float lo = 0f, hi = 1f;
|
||||
for (int i = 0; i < 24; i++)
|
||||
{
|
||||
float mid = 0.5f * (lo + hi);
|
||||
if (Mathf.Clamp01(response.Evaluate(mid)) < target) lo = mid; else hi = mid;
|
||||
}
|
||||
return 0.5f * (lo + hi);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user