using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class MainMenuManager : MonoBehaviour { [Header("Панели")] public GameObject mainMenuPanel; public GameObject headerContainer; public GameObject settingsPanel; public GameObject loadingPanel; [Header("Загрузка")] public string gameSceneName = "SampleScene"; public Slider loadingSlider; public Text loadingText; [Header("Настройки: Звук")] public Slider volumeSlider; public Text volumeValueText; public Toggle muteToggle; [Header("Настройки: Графика")] public Dropdown qualityDropdown; public Toggle fullscreenToggle; public Dropdown resolutionDropdown; [Header("Настройки: Управление")] public Slider sensitivitySlider; public Text sensitivityValueText; private Resolution[] availableResolutions; private List filteredResolutions = new List(); private bool isInitializing = false; private void Awake() { // Разблокируем курсор и гарантируем нормальный ход времени Time.timeScale = 1f; Cursor.lockState = CursorLockMode.None; Cursor.visible = true; } private void Start() { isInitializing = true; if (mainMenuPanel != null) mainMenuPanel.SetActive(true); if (settingsPanel != null) settingsPanel.SetActive(false); if (loadingPanel != null) loadingPanel.SetActive(false); InitializeSettingsUI(); isInitializing = false; } #region Основное меню public void PlayGame() { if (!string.IsNullOrEmpty(gameSceneName)) { StartCoroutine(LoadSceneRoutine(gameSceneName)); } else { SceneManager.LoadScene(1); } } public void OpenSettings() { if (mainMenuPanel != null) mainMenuPanel.SetActive(false); if (headerContainer != null) headerContainer.SetActive(false); if (settingsPanel != null) settingsPanel.SetActive(true); } public void CloseSettings() { PlayerPrefs.Save(); if (settingsPanel != null) settingsPanel.SetActive(false); if (headerContainer != null) headerContainer.SetActive(true); if (mainMenuPanel != null) mainMenuPanel.SetActive(true); } public void QuitGame() { PlayerPrefs.Save(); Debug.Log("[MainMenu] Выход из игры вызван."); Application.Quit(); #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #endif } private IEnumerator LoadSceneRoutine(string sceneName) { if (loadingPanel != null) { loadingPanel.SetActive(true); if (mainMenuPanel != null) mainMenuPanel.SetActive(false); if (settingsPanel != null) settingsPanel.SetActive(false); } AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(sceneName); if (asyncLoad == null) { Debug.LogError($"[MainMenu] Не удалось запустить загрузку сцены '{sceneName}'."); yield break; } asyncLoad.allowSceneActivation = false; while (!asyncLoad.isDone) { float progress = Mathf.Clamp01(asyncLoad.progress / 0.9f); if (loadingSlider != null) { loadingSlider.value = progress; } if (loadingText != null) { loadingText.text = $"Загрузка операции... {Mathf.RoundToInt(progress * 100)}%"; } // Когда загрузка завершена на 90% (внутренний предел Unity до активации) if (asyncLoad.progress >= 0.9f) { if (loadingText != null) { loadingText.text = "Инициализация локации..."; } yield return new WaitForSecondsRealtime(0.35f); asyncLoad.allowSceneActivation = true; } yield return null; } } #endregion #region Настройки private void InitializeSettingsUI() { // 1. Громкость и Mute float savedVolume = PlayerPrefs.GetFloat(GameSettings.KeyMasterVolume, 1.0f); bool savedMute = PlayerPrefs.GetInt(GameSettings.KeyIsMuted, 0) == 1; if (volumeSlider != null) { volumeSlider.minValue = 0f; volumeSlider.maxValue = 1f; volumeSlider.value = savedVolume; volumeSlider.onValueChanged.AddListener(OnVolumeChanged); } if (muteToggle != null) { muteToggle.isOn = savedMute; muteToggle.onValueChanged.AddListener(OnMuteChanged); } UpdateVolumeLabel(savedVolume, savedMute); AudioListener.volume = savedMute ? 0f : savedVolume; // 2. Качество графики if (qualityDropdown != null) { qualityDropdown.ClearOptions(); List qualityOptions = new List(); string[] names = QualitySettings.names; // Локализованные названия или имена из проекта for (int i = 0; i < names.Length; i++) { qualityOptions.Add(TranslateQualityName(names[i])); } qualityDropdown.AddOptions(qualityOptions); int currentQuality = PlayerPrefs.GetInt(GameSettings.KeyQualityLevel, QualitySettings.GetQualityLevel()); currentQuality = Mathf.Clamp(currentQuality, 0, names.Length - 1); qualityDropdown.value = currentQuality; qualityDropdown.RefreshShownValue(); qualityDropdown.onValueChanged.AddListener(OnQualityChanged); } // 3. Полноэкранный режим bool isFullscreen = PlayerPrefs.GetInt(GameSettings.KeyFullscreen, Screen.fullScreen ? 1 : 0) == 1; if (fullscreenToggle != null) { fullscreenToggle.isOn = isFullscreen; fullscreenToggle.onValueChanged.AddListener(OnFullscreenChanged); } // 4. Разрешения экрана PopulateResolutions(); // 5. Чувствительность мыши float savedSens = PlayerPrefs.GetFloat(GameSettings.KeyMouseSensitivity, 1.0f); if (sensitivitySlider != null) { sensitivitySlider.minValue = 0.2f; sensitivitySlider.maxValue = 3.0f; sensitivitySlider.value = savedSens; sensitivitySlider.onValueChanged.AddListener(OnSensitivityChanged); } UpdateSensitivityLabel(savedSens); } private void PopulateResolutions() { if (resolutionDropdown == null) return; resolutionDropdown.ClearOptions(); availableResolutions = Screen.resolutions; filteredResolutions.Clear(); List options = new List(); int currentResolutionIndex = 0; int currentWidth = Screen.currentResolution.width; int currentHeight = Screen.currentResolution.height; int savedWidth = PlayerPrefs.GetInt(GameSettings.KeyResolutionWidth, currentWidth); int savedHeight = PlayerPrefs.GetInt(GameSettings.KeyResolutionHeight, currentHeight); // Фильтруем дубликаты разрешений (с разной частотой обновления) HashSet seen = new HashSet(); for (int i = availableResolutions.Length - 1; i >= 0; i--) { var res = availableResolutions[i]; string key = $"{res.width}x{res.height}"; if (!seen.Contains(key)) { seen.Add(key); filteredResolutions.Insert(0, res); } } // Если разрешений нет (например в некоторых режимах редактора), добавляем стандартные if (filteredResolutions.Count == 0) { Resolution fallback = new Resolution { width = 1920, height = 1080 }; filteredResolutions.Add(fallback); } for (int i = 0; i < filteredResolutions.Count; i++) { var res = filteredResolutions[i]; string option = $"{res.width} × {res.height}"; options.Add(option); if (res.width == savedWidth && res.height == savedHeight) { currentResolutionIndex = i; } } resolutionDropdown.AddOptions(options); resolutionDropdown.value = currentResolutionIndex; resolutionDropdown.RefreshShownValue(); resolutionDropdown.onValueChanged.AddListener(OnResolutionChanged); } public void OnVolumeChanged(float val) { if (isInitializing) return; PlayerPrefs.SetFloat(GameSettings.KeyMasterVolume, val); bool isMuted = muteToggle != null && muteToggle.isOn; if (!isMuted) { AudioListener.volume = val; } UpdateVolumeLabel(val, isMuted); } public void OnMuteChanged(bool isMuted) { if (isInitializing) return; PlayerPrefs.SetInt(GameSettings.KeyIsMuted, isMuted ? 1 : 0); float currentVol = volumeSlider != null ? volumeSlider.value : 1.0f; AudioListener.volume = isMuted ? 0f : currentVol; UpdateVolumeLabel(currentVol, isMuted); } private void UpdateVolumeLabel(float volume, bool isMuted) { if (volumeValueText != null) { if (isMuted) { volumeValueText.text = "ВЫКЛ"; } else { volumeValueText.text = $"{Mathf.RoundToInt(volume * 100)}%"; } } } public void OnQualityChanged(int index) { if (isInitializing) return; QualitySettings.SetQualityLevel(index, true); PlayerPrefs.SetInt(GameSettings.KeyQualityLevel, index); } public void OnFullscreenChanged(bool isFullscreen) { if (isInitializing) return; Screen.fullScreen = isFullscreen; PlayerPrefs.SetInt(GameSettings.KeyFullscreen, isFullscreen ? 1 : 0); } public void OnResolutionChanged(int index) { if (isInitializing) return; if (index < 0 || index >= filteredResolutions.Count) return; Resolution res = filteredResolutions[index]; bool isFullscreen = fullscreenToggle != null ? fullscreenToggle.isOn : Screen.fullScreen; Screen.SetResolution(res.width, res.height, isFullscreen ? FullScreenMode.FullScreenWindow : FullScreenMode.Windowed); PlayerPrefs.SetInt(GameSettings.KeyResolutionWidth, res.width); PlayerPrefs.SetInt(GameSettings.KeyResolutionHeight, res.height); } public void OnSensitivityChanged(float val) { if (isInitializing) return; PlayerPrefs.SetFloat(GameSettings.KeyMouseSensitivity, val); UpdateSensitivityLabel(val); } private void UpdateSensitivityLabel(float val) { if (sensitivityValueText != null) { sensitivityValueText.text = $"{val:F1}x"; } } public void ResetToDefaults() { isInitializing = true; // Сброс звука if (volumeSlider != null) volumeSlider.value = 1.0f; if (muteToggle != null) muteToggle.isOn = false; AudioListener.volume = 1.0f; PlayerPrefs.SetFloat(GameSettings.KeyMasterVolume, 1.0f); PlayerPrefs.SetInt(GameSettings.KeyIsMuted, 0); UpdateVolumeLabel(1.0f, false); // Сброс графики (максимальное качество) int highestQuality = QualitySettings.names.Length - 1; if (qualityDropdown != null) { qualityDropdown.value = highestQuality; qualityDropdown.RefreshShownValue(); } QualitySettings.SetQualityLevel(highestQuality, true); PlayerPrefs.SetInt(GameSettings.KeyQualityLevel, highestQuality); // Сброс полного экрана if (fullscreenToggle != null) { fullscreenToggle.isOn = true; } Screen.fullScreen = true; PlayerPrefs.SetInt(GameSettings.KeyFullscreen, 1); // Сброс разрешения (родное) if (resolutionDropdown != null && filteredResolutions.Count > 0) { int lastIndex = filteredResolutions.Count - 1; resolutionDropdown.value = lastIndex; resolutionDropdown.RefreshShownValue(); var res = filteredResolutions[lastIndex]; Screen.SetResolution(res.width, res.height, FullScreenMode.FullScreenWindow); PlayerPrefs.SetInt(GameSettings.KeyResolutionWidth, res.width); PlayerPrefs.SetInt(GameSettings.KeyResolutionHeight, res.height); } // Сброс чувствительности if (sensitivitySlider != null) { sensitivitySlider.value = 1.0f; } PlayerPrefs.SetFloat(GameSettings.KeyMouseSensitivity, 1.0f); UpdateSensitivityLabel(1.0f); PlayerPrefs.Save(); isInitializing = false; } private string TranslateQualityName(string unityName) { switch (unityName.ToLower()) { case "very low": return "Очень низкое"; case "low": return "Низкое"; case "medium": return "Среднее"; case "high": return "Высокое"; case "very high": return "Очень высокое"; case "ultra": return "Ультра"; default: return unityName; } } #endregion }