using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class SettingsScreen : MonoBehaviour { [Header("Управление")] [SerializeField] private InSignal NextSetting; [SerializeField] private InSignal PreviousSetting; [SerializeField] private InSignal ChangeSettingPositive; [SerializeField] private InSignal ChangeSettingNegative; private bool controlsEnabled = true; private bool readyToGetInput = false; [Header("Ссылки")] [SerializeField] private SettingGroupView GroupViewPrefab; [SerializeField] private RectTransform pointer; [SerializeField] private ScrollRect scrollRect; [SerializeField] private RectTransform Content; [Header("Настройки scroll view")] [SerializeField] private float scrollSmoothTime = 0.2f; private int interactionDirection = 0; private SettingView SelectedSetting => settingsGroups[activeGroup].GeneratedSettingViews[activeSetting]; private bool Generated = false; private int activeGroup; private int activeSetting; private List settingsGroups; // Для плавного скролла private float targetVerticalPosition = 0f; private float scrollVelocity = 0f; private bool isFirstFrame = true; private void Update() { if (!Generated) return; if (isFirstFrame) { isFirstFrame = false; scrollRect.verticalNormalizedPosition = 1f; UpdateScrollTarget(); return; } } private void LateUpdate() { if (controlsEnabled && readyToGetInput) { SelectedSetting.InteractView(interactionDirection); } SmoothScrollToSelected(); DisplayPointer(); interactionDirection = 0; } public void _ChangeInteractionDirection(int delta) => interactionDirection += delta; public void EnableControls() => controlsEnabled = true; public void DisableControls() => controlsEnabled = false; public void SetReadyToGetInput() => readyToGetInput = true; public void Generate() { // Очищаем старые группы if (settingsGroups != null) { foreach (var group in settingsGroups) { if (group != null) Destroy(group.gameObject); } } settingsGroups = new List(); foreach (var group in SettingsManager.currentSchemeDTO.groups) { var generatedGroup = GenerateGroup(group); settingsGroups.Add(generatedGroup); } activeGroup = 0; activeSetting = 0; Generated = true; } public void SwitchItem(int direction) { if (direction == 0 || !controlsEnabled) return; activeSetting += direction; SettingGroupView currentGroup = settingsGroups[activeGroup]; //Выход за пределы группы (верхний предел) if (activeSetting > currentGroup.GeneratedSettingViews.Count - 1) { activeGroup = (activeGroup + 1 + settingsGroups.Count) % settingsGroups.Count; activeSetting = 0; } //Выход за пределы группы (нижний предел) else if (activeSetting < 0) { activeGroup = (activeGroup - 1 + settingsGroups.Count) % settingsGroups.Count; activeSetting = settingsGroups[activeGroup].GeneratedSettingViews.Count - 1; } UpdateScrollTarget(); } private void DisplayPointer() { pointer.SetParent(SelectedSetting.transform); pointer.localPosition = Vector3.zero; } private SettingGroupView GenerateGroup(SettingsGroupDTO group) { var go = Instantiate(GroupViewPrefab.gameObject, Content); var groupView = go.GetComponent(); groupView.Generate(group); return groupView; } private void UpdateScrollTarget() { float targetNormalizedPosition = CalculateNormalizedPosition(); targetVerticalPosition = targetNormalizedPosition; } private float CalculateNormalizedPosition() { // Проверка наличия необходимых компонентов if (scrollRect.viewport == null) { Debug.LogError("ScrollRect Viewport is not assigned!"); return 0f; } RectTransform selectedRect = SelectedSetting.GetComponent(); // Получаем мировые координаты углов Vector3[] worldCorners = new Vector3[4]; selectedRect.GetWorldCorners(worldCorners); // Получаем локальные координаты относительно Content Vector3 localPos = Content.InverseTransformPoint(worldCorners[1]); // Верхний левый угол элемента // Вычисляем нормализованную позицию (0 = вверху, 1 = внизу) float contentHeight = Content.rect.height; float viewportHeight = scrollRect.viewport.rect.height; if (contentHeight <= viewportHeight) return 0f; float maxScroll = contentHeight - viewportHeight; float scrollPosition = -localPos.y; // Инвертируем, т.к. UI координаты Y вниз // Центрируем элемент float elementHeight = selectedRect.rect.height; scrollPosition -= (viewportHeight / 2f) - (elementHeight / 2f); float normalizedPosition = Mathf.Clamp01(scrollPosition / maxScroll); return 1f - normalizedPosition; // Инвертируем для ScrollRect } private void SmoothScrollToSelected() { float currentPos = scrollRect.verticalNormalizedPosition; float newPos = Mathf.SmoothDamp(currentPos, targetVerticalPosition, ref scrollVelocity, scrollSmoothTime); scrollRect.verticalNormalizedPosition = newPos; } }