595 lines
24 KiB
C#
595 lines
24 KiB
C#
using System.Collections;
|
||
using System.Collections.Generic;
|
||
using System.Reflection;
|
||
using TMPro;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
|
||
public class SettingsScreen : MonoBehaviour
|
||
{
|
||
[Header("Панель с опциями")]
|
||
[SerializeField] RectTransform OptionsRect;
|
||
|
||
[Header("Панель с вариантами опций")]
|
||
[SerializeField] RectTransform ValuesRect;
|
||
|
||
[Header("Панель с статистикой")]
|
||
[SerializeField] CountersView StatisticRect;
|
||
|
||
|
||
[Header("Указатель опций")]
|
||
[SerializeField] RectTransform PointerRect;
|
||
|
||
[Header("Префаб текста настройки")]
|
||
[SerializeField] GameObject SettingPrefab;
|
||
|
||
[Header("Префаб текста опции")]
|
||
[SerializeField] GameObject OptionPrefab;
|
||
|
||
[Header("Префаб прогресс-бара (для Panel/Action)")]
|
||
[SerializeField] GameObject ProgressBarPrefab;
|
||
|
||
[Header("Префаб слайдера (для Svitcher)")]
|
||
[SerializeField] GameObject SwitcherSliderPrefab;
|
||
|
||
[Header("Настройки прокрутки")]
|
||
[SerializeField] int maxVisibleSettings = 5; // максимально видимых элементов
|
||
[SerializeField] float scrollSpeed = 10f; // скорость сглаживания прокрутки
|
||
|
||
// Скроллбар UI
|
||
[Header("UI Scrollbar")]
|
||
[SerializeField] Scrollbar Scrollbar;
|
||
|
||
List<GameObject> settingsLabels;
|
||
List<GameObject> optionsLabels;
|
||
Dictionary<int, Image> progressBars; // индекс настройки -> прогресс-бар
|
||
Dictionary<int, TextMeshProUGUI> progressTexts; // индекс настройки -> текст процентов
|
||
Dictionary<int, Slider> switcherSliders; // индекс настройки -> слайдер для Svitcher
|
||
|
||
substate currentState;
|
||
|
||
List<SettingOption> gameSettings => SettingsManager.instance.gameSettings.settings;
|
||
int settingsAmount => SettingsManager.instance.gameSettings.settings.Count;
|
||
List<string> chosenSettingOptions => SettingsManager.instance.gameSettings.settings[currentSettingIndex].options;
|
||
int chosenSettingOptionsAmount => chosenSettingOptions.Count;
|
||
|
||
|
||
public int currentSettingIndex = 0;
|
||
int currentOptionIndex = 0;
|
||
|
||
|
||
float startx = 50;
|
||
float starty = -50;
|
||
|
||
//float offsetx = 100;
|
||
float offsetx = 0;
|
||
float offsety = 70;
|
||
|
||
Coroutine chooseOption;
|
||
|
||
[SerializeField] float optionsPadding = 20;
|
||
|
||
// Переменные для прокрутки
|
||
int scrollOffset = 0; // текущее смещение (индекс верхнего видимого элемента)
|
||
float targetScrollY = 0f; // целевая позиция прокрутки
|
||
float currentScrollY = 0f; // текущая позиция прокрутки (интерполируемая)
|
||
|
||
// Вспомогательные переменные для скроллбара
|
||
int maxScrollSteps = 0; // settingsAmount - maxVisibleSettings
|
||
|
||
public void Open()
|
||
{
|
||
optionsLabels = new List<GameObject>();
|
||
settingsLabels = new List<GameObject>();
|
||
progressBars = new Dictionary<int, Image>();
|
||
progressTexts = new Dictionary<int, TextMeshProUGUI>();
|
||
switcherSliders = new Dictionary<int, Slider>();
|
||
PointerRect.gameObject.SetActive(true);
|
||
currentState = substate.SettingChoosing;
|
||
currentSettingIndex = 0;
|
||
|
||
// обнуляем прокрутку
|
||
scrollOffset = 0;
|
||
targetScrollY = 0f;
|
||
currentScrollY = 0f;
|
||
|
||
CreateSettingsLabels();
|
||
StatisticRect.Initialize();
|
||
|
||
// Подписка на скроллбар
|
||
if (Scrollbar != null)
|
||
{
|
||
// вычислим параметры
|
||
maxScrollSteps = Mathf.Max(0, settingsAmount - maxVisibleSettings);
|
||
Scrollbar.gameObject.SetActive(maxScrollSteps > 0);
|
||
if (maxScrollSteps > 0)
|
||
{
|
||
// размер «видимого окна» на скроллбаре
|
||
Scrollbar.size = (float)maxVisibleSettings / (float)settingsAmount;
|
||
float value = (maxScrollSteps > 0) ? 1f - (float)scrollOffset / (float)maxScrollSteps : 1f;
|
||
Scrollbar.SetValueWithoutNotify(value);
|
||
Scrollbar.onValueChanged.AddListener(OnScrollbarChanged);
|
||
}
|
||
else
|
||
{
|
||
Scrollbar.onValueChanged.RemoveListener(OnScrollbarChanged);
|
||
}
|
||
}
|
||
}
|
||
|
||
public void Close()
|
||
{
|
||
// отписываемся от событий скроллбара
|
||
if (Scrollbar != null)
|
||
{
|
||
Scrollbar.onValueChanged.RemoveListener(OnScrollbarChanged);
|
||
}
|
||
|
||
ClearOptionsPanel();
|
||
ClearSettingsPanel();
|
||
if (chooseOption != null) StopCoroutine(chooseOption);
|
||
}
|
||
|
||
public void Switch(int direction)
|
||
{
|
||
switch (currentState)
|
||
{
|
||
case substate.SettingChoosing:
|
||
currentSettingIndex = (currentSettingIndex + settingsAmount + direction) % settingsAmount;
|
||
UpdateScrollPosition();
|
||
// не устанавливаем pointer напрямую на целевой, пусть он позиционируется относительно currentScrollY
|
||
UpdatePointerPosition();
|
||
break;
|
||
case substate.OptionChoosing:
|
||
currentOptionIndex = (currentOptionIndex + chosenSettingOptionsAmount + direction) % chosenSettingOptionsAmount;//ТУТ НАДО КОЛИЧЕСТВО ОПЦИЙ НАСТРОЙКИ ПОЛУЧИТЬ
|
||
break;
|
||
}
|
||
//SoundManager.instance.playSwitch();
|
||
}
|
||
|
||
//менять состояние, сохранять данные
|
||
public void Accept()
|
||
{
|
||
if (currentState == substate.SettingChoosing)
|
||
{
|
||
createOptionsLabels(gameSettings[currentSettingIndex]);
|
||
currentState = substate.OptionChoosing;
|
||
PointerRect.gameObject.SetActive(false);
|
||
|
||
if (chooseOption != null) StopCoroutine(chooseOption);
|
||
chooseOption = StartCoroutine(DisplayChosenOption());
|
||
}
|
||
|
||
else if (currentState == substate.OptionChoosing)
|
||
{
|
||
SettingsManager.instance.ChangeSetting(settingName: gameSettings[currentSettingIndex].settingName,
|
||
newIndex: currentOptionIndex);
|
||
|
||
PointerRect.gameObject.SetActive(true);
|
||
ClearOptionsPanel();
|
||
//Обновляем для учета изменений
|
||
ClearSettingsPanel();
|
||
CreateSettingsLabels();
|
||
|
||
if (chooseOption != null) StopCoroutine(chooseOption);
|
||
|
||
currentState = substate.SettingChoosing;
|
||
}
|
||
|
||
//SoundManager.instance.playChoose();
|
||
|
||
}
|
||
|
||
public void AcceptView()
|
||
{
|
||
StatisticRect.View();
|
||
}
|
||
|
||
/// <summary>
|
||
/// Можно ли открыть панель или выполнить экшн для текущей настройки
|
||
/// </summary>
|
||
public bool CanOpenPanel()
|
||
{
|
||
if (currentState != substate.SettingChoosing) return false;
|
||
if (currentSettingIndex < 0 || currentSettingIndex >= gameSettings.Count) return false;
|
||
|
||
SettingType type = gameSettings[currentSettingIndex].type;
|
||
return type == SettingType.Panel || type == SettingType.Action;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает ID панели для текущей выбранной настройки (-1 если не панель)
|
||
/// </summary>
|
||
public int GetCurrentPanelId()
|
||
{
|
||
if (currentState != substate.SettingChoosing) return -1;
|
||
if (currentSettingIndex < 0 || currentSettingIndex >= gameSettings.Count) return -1;
|
||
return gameSettings[currentSettingIndex].panelId;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Возвращает тип текущей выбранной настройки
|
||
/// </summary>
|
||
public SettingType GetCurrentSettingType()
|
||
{
|
||
if (currentState != substate.SettingChoosing) return SettingType.Normal;
|
||
if (currentSettingIndex < 0 || currentSettingIndex >= gameSettings.Count) return SettingType.Normal;
|
||
return gameSettings[currentSettingIndex].type;
|
||
}
|
||
|
||
/// <summary>
|
||
/// Обновляет заполнение прогресс-бара для текущей настройки (если она типа Panel/Action)
|
||
/// </summary>
|
||
public void UpdateProgressBar(float fillAmount)
|
||
{
|
||
fillAmount = Mathf.Clamp01(fillAmount);
|
||
|
||
if (progressBars.TryGetValue(currentSettingIndex, out Image progressBar))
|
||
{
|
||
progressBar.fillAmount = fillAmount;
|
||
}
|
||
|
||
if (progressTexts.TryGetValue(currentSettingIndex, out TextMeshProUGUI progressText))
|
||
{
|
||
// Показываем текст только если прогресс-бар заполняется
|
||
if (fillAmount > 0f)
|
||
{
|
||
progressText.gameObject.SetActive(true);
|
||
int percentage = Mathf.RoundToInt(fillAmount * 100f);
|
||
progressText.text = $"{percentage}%";
|
||
}
|
||
else
|
||
{
|
||
progressText.gameObject.SetActive(false);
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
//Шерстим все существующие настройки
|
||
//Создаем для каждой настройки текстовое поле
|
||
private void CreateSettingsLabels()
|
||
{
|
||
// ...existing code...
|
||
settingsLabels = new List<GameObject>();
|
||
for (int i = 0; i < SettingsManager.instance.gameSettings.settings.Count; i++)
|
||
{
|
||
createLabelFromSettingOption(SettingsManager.instance.gameSettings.settings[i], i);
|
||
}
|
||
|
||
// Обновляем прокрутку и позиции после создания
|
||
UpdateScrollPosition();
|
||
currentScrollY = targetScrollY;
|
||
UpdateSettingsPositions();
|
||
|
||
//Процедура которая собственно создает лейблы
|
||
void createLabelFromSettingOption(SettingOption option, int index)
|
||
{
|
||
GameObject label = Instantiate(SettingPrefab, OptionsRect);
|
||
label.name = option.settingName + " Setting";
|
||
settingsLabels.Add(label);
|
||
RectTransform rt = label.GetComponent<RectTransform>();
|
||
|
||
// Добавляем CanvasGroup для управления видимостью
|
||
CanvasGroup canvasGroup = rt.GetComponent<CanvasGroup>();
|
||
if (canvasGroup == null) canvasGroup = rt.gameObject.AddComponent<CanvasGroup>();
|
||
|
||
// Изначальная позиция будет обновлена в UpdateSettingsPositions()
|
||
rt.anchoredPosition = new Vector2(startx + offsetx * index, starty - offsety * index);
|
||
|
||
//Загоняем название настройки
|
||
TextMeshProUGUI text = rt.gameObject.GetComponent<TextMeshProUGUI>();
|
||
text.text = option.settingName;
|
||
|
||
// Для Panel/Action создаём прогресс-бар, для Svitcher — слайдер, для Normal — текст опц
|
||
if (option.type == SettingType.Panel || option.type == SettingType.Action)
|
||
{
|
||
if (ProgressBarPrefab != null)
|
||
{
|
||
GameObject progressBarObj = Instantiate(ProgressBarPrefab, rt);
|
||
progressBarObj.name = option.settingName + " ProgressBar";
|
||
RectTransform progressRT = progressBarObj.GetComponent<RectTransform>();
|
||
progressRT.anchoredPosition = new Vector2(rt.sizeDelta.x, 0);
|
||
|
||
Image progressImage = progressBarObj.GetComponentInChildren<Image>();
|
||
if (progressImage != null)
|
||
{
|
||
progressImage.fillAmount = 0f;
|
||
progressBars[index] = progressImage;
|
||
}
|
||
|
||
// Находим TextMeshProUGUI для отображения процентов
|
||
TextMeshProUGUI progressText = progressBarObj.GetComponentInChildren<TextMeshProUGUI>();
|
||
if (progressText != null)
|
||
{
|
||
progressText.text = "0%";
|
||
progressText.gameObject.SetActive(false); // скрываем до начала заполнения
|
||
progressTexts[index] = progressText;
|
||
}
|
||
}
|
||
}
|
||
else if (option.type == SettingType.Svitcher)
|
||
{
|
||
// Создаём слайдер для переключателя
|
||
if (SwitcherSliderPrefab != null)
|
||
{
|
||
GameObject sliderObj = Instantiate(SwitcherSliderPrefab, rt);
|
||
sliderObj.name = option.settingName + " Slider";
|
||
RectTransform sliderRT = sliderObj.GetComponent<RectTransform>();
|
||
sliderRT.anchoredPosition = new Vector2(rt.sizeDelta.x, 0);
|
||
|
||
Slider slider = sliderObj.GetComponent<Slider>();
|
||
if (slider != null)
|
||
{
|
||
// Устанавливаем начальное значение: инвертируем для корректного отображения
|
||
// 0 (выкл визуально) = индекс 1 ("Выкл"), 1 (вкл визуально) = индекс 0 ("Вкл")
|
||
slider.minValue = 0;
|
||
slider.maxValue = 1;
|
||
|
||
// ВАЖНО: используем SetValueWithoutNotify, чтобы не вызывать событие при инициализации
|
||
int initialValue = (option.chosenIndex == 0) ? 1 : 0;
|
||
slider.SetValueWithoutNotify(initialValue);
|
||
|
||
// Сохраняем ссылку на слайдер
|
||
switcherSliders[index] = slider;
|
||
|
||
// НЕ подписываемся на onValueChanged - сохранение происходит через callback в ToggleSwitcher
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Обычная настройка — показываем текущую опцию
|
||
GameObject label2 = Instantiate(OptionPrefab, rt);
|
||
label2.name = option.settingName + " Value";
|
||
RectTransform option_rt = label2.GetComponent<RectTransform>();
|
||
option_rt.anchoredPosition = new Vector2(rt.sizeDelta.x, 0);
|
||
|
||
text = option_rt.gameObject.GetComponent<TextMeshProUGUI>();
|
||
text.text = option.ChosenOption;
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
// Вычисляем и задаём смещение прокрутки и целевую позицию
|
||
private void UpdateScrollPosition()
|
||
{
|
||
if (settingsAmount <= maxVisibleSettings)
|
||
{
|
||
scrollOffset = 0;
|
||
targetScrollY = 0f;
|
||
if (Scrollbar != null) Scrollbar.gameObject.SetActive(false);
|
||
return;
|
||
}
|
||
|
||
if (currentSettingIndex >= scrollOffset + maxVisibleSettings)
|
||
{
|
||
scrollOffset = currentSettingIndex - maxVisibleSettings + 1;
|
||
}
|
||
else if (currentSettingIndex < scrollOffset)
|
||
{
|
||
scrollOffset = currentSettingIndex;
|
||
}
|
||
|
||
targetScrollY = scrollOffset * offsety;
|
||
|
||
// обновим скроллбар без вызова слушателя
|
||
if (Scrollbar != null && maxScrollSteps > 0)
|
||
{
|
||
float value = 1f - (float)scrollOffset / (float)maxScrollSteps;
|
||
Scrollbar.SetValueWithoutNotify(value);
|
||
}
|
||
}
|
||
|
||
// Обновляем позицию указателя относительно видимой области
|
||
private void UpdatePointerPosition()
|
||
{
|
||
// Используем плавающее смещение, чтобы указатель плавно следовал за прокруткой
|
||
float scrollFloat = (offsety != 0) ? currentScrollY / offsety : 0f;
|
||
float visibleIndex = currentSettingIndex - scrollFloat;
|
||
PointerRect.anchoredPosition = new Vector2(startx + 35 + currentSettingIndex * offsetx, starty - visibleIndex * offsety);
|
||
}
|
||
|
||
private void UpdateSettingsPositions()
|
||
{
|
||
if (settingsLabels == null) return;
|
||
|
||
// Плавающее смещение в строках
|
||
float scrollFloat = (offsety != 0) ? currentScrollY / offsety : 0f;
|
||
|
||
for (int i = 0; i < settingsLabels.Count; i++)
|
||
{
|
||
RectTransform rt = settingsLabels[i].GetComponent<RectTransform>();
|
||
|
||
// Вычисляем видимую позицию с учётом плавающего смещения
|
||
float visiblePosition = i - scrollFloat;
|
||
float targetY = starty - visiblePosition * offsety;
|
||
|
||
rt.anchoredPosition = new Vector2(startx + offsetx * i, targetY);
|
||
|
||
// Можно также добавить прозрачность для невидимых элементов
|
||
CanvasGroup canvasGroup = rt.GetComponent<CanvasGroup>();
|
||
if (canvasGroup == null) canvasGroup = rt.gameObject.AddComponent<CanvasGroup>();
|
||
|
||
// Элементы вне видимой области делаем невидимыми
|
||
if (i < scrollOffset || i >= scrollOffset + maxVisibleSettings)
|
||
{
|
||
canvasGroup.alpha = 0f;
|
||
canvasGroup.interactable = false;
|
||
canvasGroup.blocksRaycasts = false;
|
||
}
|
||
else
|
||
{
|
||
canvasGroup.alpha = 1f;
|
||
canvasGroup.interactable = true;
|
||
canvasGroup.blocksRaycasts = true;
|
||
}
|
||
}
|
||
|
||
// Обновляем позицию указателя
|
||
UpdatePointerPosition();
|
||
}
|
||
|
||
// обрабтчик изменения значения Scrollbar (0..1). Значение 1 = верх (scrollOffset=0), 0 = низ (максимальное смещение)
|
||
private void OnScrollbarChanged(float value)
|
||
{
|
||
if (settingsAmount <= maxVisibleSettings) return;
|
||
maxScrollSteps = Mathf.Max(0, settingsAmount - maxVisibleSettings);
|
||
if (maxScrollSteps == 0) return;
|
||
|
||
// инвертируем значение: 1 -> 0, 0 -> maxScrollSteps
|
||
float t = 1f - value;
|
||
float desiredOffsetF = t * maxScrollSteps;
|
||
|
||
// Применяем дробную цель к targetScrollY (плавнее)
|
||
targetScrollY = desiredOffsetF * offsety;
|
||
// и обновляем целевой scrollOffset целым числом для маски видимости
|
||
scrollOffset = Mathf.Clamp(Mathf.RoundToInt(desiredOffsetF), 0, maxScrollSteps);
|
||
}
|
||
|
||
void Update()
|
||
{
|
||
// Плавная прокрутка
|
||
if (currentState == substate.SettingChoosing && Mathf.Abs(currentScrollY - targetScrollY) > 0.1f)
|
||
{
|
||
currentScrollY = Mathf.Lerp(currentScrollY, targetScrollY, Time.deltaTime * scrollSpeed);
|
||
UpdateSettingsPositions();
|
||
|
||
// при программной прокрутке обновляем скроллбар, чтобы отражать текущее положение
|
||
if (Scrollbar != null && maxScrollSteps > 0)
|
||
{
|
||
float scrollFloat = currentScrollY / offsety; // может быть дробным
|
||
float value = 1f - (scrollFloat / (float)maxScrollSteps);
|
||
// защита от NaN/Inf
|
||
if (float.IsNaN(value) || float.IsInfinity(value)) value = 1f;
|
||
Scrollbar.SetValueWithoutNotify(Mathf.Clamp01(value));
|
||
}
|
||
}
|
||
else if (currentScrollY != targetScrollY)
|
||
{
|
||
currentScrollY = targetScrollY;
|
||
UpdateSettingsPositions();
|
||
}
|
||
}
|
||
|
||
// Опции настроек
|
||
void createOptionsLabels(SettingOption option)
|
||
{
|
||
if (optionsLabels == null) optionsLabels = new List<GameObject>();
|
||
currentOptionIndex = 0;
|
||
|
||
float labelWidth = (ValuesRect.sizeDelta.x - optionsPadding * 2) / option.options.Count;
|
||
float labelHeight = ValuesRect.sizeDelta.y - optionsPadding * 2;
|
||
print(ValuesRect.sizeDelta);
|
||
|
||
foreach (string optionName in option.options)
|
||
{
|
||
GameObject label = Instantiate(OptionPrefab, ValuesRect);
|
||
optionsLabels.Add(label);
|
||
|
||
RectTransform rt = label.GetComponent<RectTransform>();
|
||
rt.pivot = new Vector2(0, 1f);
|
||
rt.sizeDelta = new Vector2(labelWidth, labelHeight);
|
||
rt.anchoredPosition = new Vector2(optionsPadding + labelWidth * currentOptionIndex, optionsPadding);
|
||
|
||
|
||
TextMeshProUGUI text = label.GetComponent<TextMeshProUGUI>();
|
||
text.text = optionName;
|
||
text.alignment = TextAlignmentOptions.Center;
|
||
text.color = Color.gray;
|
||
currentOptionIndex++;
|
||
}
|
||
currentOptionIndex = option.chosenIndex;
|
||
}
|
||
void ClearSettingsPanel()
|
||
{
|
||
if (settingsLabels == null) return;
|
||
foreach (GameObject label in settingsLabels)
|
||
{
|
||
if (label != null) Object.Destroy(label);
|
||
}
|
||
settingsLabels.Clear();
|
||
}
|
||
void ClearOptionsPanel()
|
||
{
|
||
if (optionsLabels == null) return;
|
||
foreach (GameObject label in optionsLabels)
|
||
{
|
||
if (label != null) Object.Destroy(label);
|
||
}
|
||
optionsLabels.Clear();
|
||
}
|
||
|
||
IEnumerator DisplayChosenOption()
|
||
{
|
||
Color normalColor = Color.gray;
|
||
Color highlightedColor = Color.white;
|
||
|
||
int normalSize = 26;
|
||
int chosenSize = 40;
|
||
|
||
List<TextMeshProUGUI> options = new List<TextMeshProUGUI>();
|
||
if (optionsLabels != null)
|
||
{
|
||
foreach (GameObject label in optionsLabels)
|
||
{
|
||
options.Add(label.GetComponent<TextMeshProUGUI>());
|
||
}
|
||
}
|
||
|
||
while (true)
|
||
{
|
||
for (int i = 0; i < options.Count; i++)
|
||
{
|
||
if (i == currentOptionIndex)
|
||
{
|
||
options[i].color = Vector4.MoveTowards(options[i].color, highlightedColor, Time.deltaTime * 5);
|
||
options[i].fontSize = Mathf.MoveTowards(options[i].fontSize, chosenSize, Time.deltaTime * 140);
|
||
}
|
||
else
|
||
{
|
||
options[i].color = Vector4.MoveTowards(options[i].color, normalColor, Time.deltaTime * 5);
|
||
options[i].fontSize = Mathf.MoveTowards(options[i].fontSize, normalSize, Time.deltaTime * 140);
|
||
}
|
||
}
|
||
yield return null;
|
||
}
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// Переключает слайдер текущей выбранной настройки (для типа Svitcher)
|
||
/// Вызывается из SettingsInput при нажатии кнопки
|
||
/// </summary>
|
||
public void ToggleSwitcher()
|
||
{
|
||
if (currentState != substate.SettingChoosing || !switcherSliders.TryGetValue(currentSettingIndex, out Slider slider)) return;
|
||
|
||
// ВАЖНО: Округляем текущее значение до 0 или 1, чтобы избежать промежуточных значений при быстрых нажатиях
|
||
int currentValue = Mathf.RoundToInt(slider.value);
|
||
float newValue = currentValue == 0 ? 1f : 0f;
|
||
string settingName = gameSettings[currentSettingIndex].settingName;
|
||
|
||
// Анимируем с callback для сохранения
|
||
SmoothSlider smoothSlider = slider.GetComponent<SmoothSlider>();
|
||
if (smoothSlider != null)
|
||
{
|
||
smoothSlider.AnimateTo(newValue, (val) =>
|
||
SettingsManager.instance.ChangeSetting(settingName, val == 1 ? 0 : 1));
|
||
}
|
||
else
|
||
{
|
||
slider.value = newValue; // Fallback без анимации
|
||
}
|
||
}
|
||
|
||
enum substate { SettingChoosing, OptionChoosing }
|
||
|
||
}
|