243 lines
7.0 KiB
C#

using System.Collections;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
public class IntView : MonoBehaviour, IView
{
[Header("Layout ссылки")]
[SerializeField] private TextMeshProUGUI ValueLabel;
[SerializeField] private GameObject LeftArrow;
[SerializeField] private GameObject RightArrow;
[Header("Анимация")]
[SerializeField] private float smoothTime = 0.1f;
[SerializeField] private AnimationCurve scaleCurve = AnimationCurve.EaseInOut(0f, 1f, 1f, 1.2f);
[Header("Ускоренное переключение")]
[SerializeField] private float initialDelay = 0.5f; // Задержка перед началом ускорения
[SerializeField] private float minRepeatDelay = 0.05f; // Минимальная задержка между изменениями
[SerializeField] private float accelerationSpeed = 0.95f; // Скорость ускорения (чем меньше, тем быстрее)
[Header("Звук")]
[SerializeField] private bool UseSound = true;
[SerializeField] private AudioClip SwitchClip;
[SerializeField] private AudioSource Source;
// Кэш значений настройки
private int minValue;
private int maxValue;
private int currentValue;
private int step = 1;
// Состояние ускоренного переключения
private Coroutine accelerationCoroutine;
private int currentDirection = 0; // -1 = влево, 0 = нет, 1 = вправо
public Setting CachedSetting { get; private set; }
public void Initialize(Setting setting)
{
CachedSetting = setting;
CacheSettingValues(setting);
if (Source != null && SwitchClip != null)
Source.clip = SwitchClip;
UpdateUI();
}
public void TakeInput(int direction)
{
if (direction != 0)
{
if (currentDirection == direction)
return;
currentDirection = direction;
ChangeValue(currentDirection);
if (accelerationCoroutine != null)
StopCoroutine(accelerationCoroutine);
accelerationCoroutine = StartCoroutine(AcceleratedSwitching());
}
else
{
if (accelerationCoroutine != null)
{
StopCoroutine(accelerationCoroutine);
accelerationCoroutine = null;
}
currentDirection = 0;
if (UseSound && Source != null && Source.isPlaying)
Source.Stop();
}
}
private IEnumerator AcceleratedSwitching()
{
float delay = initialDelay;
yield return new WaitForSeconds(delay);
// Ускоренное переключение
while (currentDirection != 0)
{
ChangeValue(currentDirection);
delay = Mathf.Max(minRepeatDelay, delay * accelerationSpeed);
yield return new WaitForSeconds(delay);
}
}
private void ChangeValue(int direction)
{
if (direction == 0) return;
int newValue = currentValue + step * (int)Mathf.Sign(direction);
bool wasAtLimit = false;
if (direction > 0 && currentValue >= maxValue)
{
wasAtLimit = true;
}
else if (direction < 0 && currentValue <= minValue)
{
wasAtLimit = true;
}
newValue = Mathf.Clamp(newValue, minValue, maxValue);
if (newValue == currentValue)
{
if (wasAtLimit)
StartCoroutine(FlashLimitIndicator(direction > 0 ? RightArrow : LeftArrow));
return;
}
currentValue = newValue;
CachedSetting.IntValue = currentValue;
AnimateValueChange();
if (UseSound)
PlaySound();
UpdateUI();
}
private void UpdateUI()
{
if (ValueLabel != null)
ValueLabel.text = currentValue.ToString();
UpdateArrows();
}
private void UpdateArrows()
{
LeftArrow.SetActive(currentValue > minValue);
RightArrow.SetActive(currentValue < maxValue);
}
private void AnimateValueChange()
{
if (ValueLabel == null) return;
StopCoroutine(nameof(ScaleAnimation));
StartCoroutine(ScaleAnimation());
}
private IEnumerator ScaleAnimation()
{
Vector3 originalScale = ValueLabel.transform.localScale;
float elapsed = 0f;
float duration = 0.2f;
// Увеличиваем
while (elapsed < duration / 2f)
{
elapsed += Time.deltaTime;
float t = elapsed / (duration / 2f);
float scale = Mathf.Lerp(1f, 1.2f, scaleCurve.Evaluate(t));
ValueLabel.transform.localScale = originalScale * scale;
yield return null;
}
elapsed = 0f;
while (elapsed < duration / 2f)
{
elapsed += Time.deltaTime;
float t = elapsed / (duration / 2f);
float scale = Mathf.Lerp(1.2f, 1f, scaleCurve.Evaluate(t));
ValueLabel.transform.localScale = originalScale * scale;
yield return null;
}
ValueLabel.transform.localScale = originalScale;
}
private IEnumerator FlashLimitIndicator(GameObject arrow)
{
if (arrow == null) yield break;
Image arrowImage = arrow.GetComponent<Image>();
if (arrowImage == null) yield break;
Color originalColor = arrowImage.color;
Color flashColor = Color.red;
arrowImage.color = flashColor;
yield return new WaitForSeconds(0.1f);
arrowImage.color = originalColor;
}
private void PlaySound()
{
if (!UseSound || Source == null) return;
// Динамический питч в зависимости от скорости переключения
if (accelerationCoroutine != null)
{
// При ускоренном переключении повышаем питч
float pitchMultiplier = Mathf.Lerp(1f, 2f,
(initialDelay - Mathf.Max(minRepeatDelay,
initialDelay * Mathf.Pow(accelerationSpeed, 10))) / initialDelay);
Source.pitch = Mathf.Clamp(0.8f + pitchMultiplier * 0.5f, 0.8f, 1.5f);
}
else
{
Source.pitch = 1f;
}
Source.PlayOneShot(SwitchClip);
}
private void CacheSettingValues(Setting setting)
{
minValue = setting.IntMin;
maxValue = setting.IntMax;
currentValue = setting.IntValue;
step = setting.IntStep;
if (step <= 0)
{
Debug.LogWarning($"Step is zero or negative ({step}), setting to 1", this);
step = 1;
}
if (minValue >= maxValue)
{
Debug.LogWarning($"Invalid range: min={minValue}, max={maxValue}", this);
}
}
private void OnDestroy()
{
if (accelerationCoroutine != null)
StopCoroutine(accelerationCoroutine);
if (Source != null && Source.isPlaying)
Source.Stop();
}
}