Files
chameleon/Assets/Scripts/Components/UI/SliderCurve.cs
T
adminandClaude Fable 5 6c03e5d3f6 Refactor Assets/Scripts: trim restating comments, minor structure cleanups
Sweep over 48 files (Editor/ excluded): remove comments that restate the
code, condense class summaries, fix three stale doc statements
(HsvColorPicker alpha bar, PaintHud layoutPrefab, MasterVolume
FallbackDefault). Inline two single-use wrappers (IsPointerOverUi,
BuildModes), drop an unused using, replace a magic literal with the
existing ParallaxBase constant, and convert ~10 UnityEngine.Object null
checks to the implicit-bool convention. No serialized fields, signatures,
RPC/NetworkVariable names, defaults, or #if blocks touched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 08:29:18 +02:00

76 lines
3.0 KiB
C#

using System;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// Reshapes a UI <see cref="Slider"/>'s response with an <see cref="AnimationCurve"/> mapping handle
/// position (x, 0..1) to a value fraction (y, 0..1) scaled to the slider's own minValue/maxValue, which
/// stays the single source of truth for the range. The curve must be monotonic increasing: 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);
}
}