using System; using TMPro; using UnityEngine; public class MNumberStepper : MonoBehaviour { public event Action OnValueChanged; [SerializeField] private TextMeshProUGUI _valueLabel; [SerializeField] private CanvasGroup _leftArrow; [SerializeField] private CanvasGroup _rightArrow; [Header("Настройки ускорения")] [SerializeField] private float _accelerationTime = 0.8f; // Время до максимальной скорости [SerializeField] private int _maxStep = 10; // Максимальный шаг [SerializeField] private float _minInterval = 0.5f; // Начальный интервал (сек) [SerializeField] private float _maxInterval = 0.03f; // Минимальный интервал (сек) [SerializeField] private AnimationCurve _accelerationCurve = AnimationCurve.EaseInOut(0, 0, 1, 1); private float _holdTime; private int _lastDirection; private bool _isHolding; private float _lastChangeTime; public int Value { get; private set; } private int _min; private int _max; public void Initialize(int value, int min, int max) { Value = value; _min = min; _max = max; ResetState(); UpdateView(); } public void SetDirection(int direction) { if (direction == 0) { // Отпустили кнопку _isHolding = false; _lastDirection = 0; _holdTime = 0f; return; } if (!_isHolding) { // Первое нажатие - мгновенно меняем значение _isHolding = true; _lastDirection = direction; _holdTime = 0f; _lastChangeTime = Time.unscaledTime; // Мгновенное изменение при первом клике ChangeValue(direction, 1); return; } // Если направление изменилось - сбрасываем ускорение if (_lastDirection != direction) { _lastDirection = direction; _holdTime = 0f; _lastChangeTime = Time.unscaledTime; return; } // Увеличиваем время удержания _holdTime += Time.unscaledDeltaTime; // Вычисляем прогресс ускорения (0 -> 1) float progress = Mathf.Clamp01(_holdTime / _accelerationTime); float curveValue = _accelerationCurve.Evaluate(progress); // 1. РАССЧИТЫВАЕМ ШАГ (увеличивается от 1 до _maxStep) int step = 1 + Mathf.RoundToInt(curveValue * (_maxStep - 1)); step = Mathf.Max(1, step); // Шаг не может быть меньше 1 // 2. РАССЧИТЫВАЕМ ИНТЕРВАЛ (уменьшается от _minInterval до _maxInterval) float currentInterval = Mathf.Lerp(_minInterval, _maxInterval, curveValue); // Проверяем, пора ли менять значение float timeSinceLastChange = Time.unscaledTime - _lastChangeTime; if (timeSinceLastChange >= currentInterval) { _lastChangeTime = Time.unscaledTime; ChangeValue(_lastDirection, step); } } private void ChangeValue(int direction, int step) { int newValue = Value + direction * step; newValue = Mathf.Clamp(newValue, _min, _max); if (newValue != Value) { Value = newValue; OnValueChanged?.Invoke(Value); UpdateView(); } else { // Если уперлись в границу - сбрасываем ускорение ResetState(); } } private void UpdateView() { _valueLabel.text = Value.ToString(); _leftArrow.alpha = Value == _min ? 0 : 1; _rightArrow.alpha = Value == _max ? 0 : 1; } public void ResetState() { _isHolding = false; _lastDirection = 0; _holdTime = 0f; _lastChangeTime = 0f; } }