using System; using UnityEngine; using UnityEngine.UI; /// /// Reshapes a UI 's response with an 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 assumes that. /// [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); /// Raised with the curved output whenever the handle moves. public event Action ValueChanged; private Slider _slider; private Slider Slider => _slider ? _slider : (_slider = GetComponent()); /// The curved output value for the current handle position. 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); /// Positions the handle so reads , raising the /// slider's onValueChanged (and thus ). public void SetOutput(float output) { if (Slider) Slider.normalizedValue = Position(output); // normalizedValue setter notifies } /// As but without raising any change event (for silent seeding). 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); } }