431 lines
14 KiB
C#
431 lines
14 KiB
C#
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<Resolution> filteredResolutions = new List<Resolution>();
|
||
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<string> qualityOptions = new List<string>();
|
||
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<string> options = new List<string>();
|
||
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<string> seen = new HashSet<string>();
|
||
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
|
||
}
|