573 lines
19 KiB
C#
573 lines
19 KiB
C#
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using UnityEngine.UI;
|
||
using UnityEngine.SceneManagement;
|
||
using UnityEngine.InputSystem;
|
||
|
||
public class PauseMenu : MonoBehaviour
|
||
{
|
||
[Header("Панели")]
|
||
public GameObject pauseMenuPanel;
|
||
public GameObject settingsPanel;
|
||
|
||
[Header("Кнопки панели действий")]
|
||
public Button resumeButton;
|
||
public Button restartButton;
|
||
public Button mainMenuButton;
|
||
public Button quitButton;
|
||
|
||
[Header("Вкладки")]
|
||
public Button[] tabButtons;
|
||
public GameObject[] tabPanels;
|
||
|
||
[Header("Настройки: Звук")]
|
||
public Slider volumeSlider;
|
||
public Text volumeValueText;
|
||
public Toggle muteToggle;
|
||
|
||
[Header("Настройки: Графика")]
|
||
public Dropdown qualityDropdown;
|
||
public Toggle fullscreenToggle;
|
||
public Dropdown resolutionDropdown;
|
||
public Toggle vsyncToggle;
|
||
|
||
[Header("Настройки: Управление")]
|
||
public Slider sensitivitySlider;
|
||
public Text sensitivityValueText;
|
||
public Toggle invertYToggle;
|
||
|
||
private bool isPaused = false;
|
||
private bool isInitializing = false;
|
||
private bool wasCursorLocked = false;
|
||
private List<Resolution> filteredResolutions = new List<Resolution>();
|
||
|
||
public bool IsPaused => isPaused;
|
||
|
||
private void Awake()
|
||
{
|
||
AutoWireUI();
|
||
}
|
||
|
||
private void Start()
|
||
{
|
||
AutoWireUI();
|
||
|
||
if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false);
|
||
if (settingsPanel != null && settingsPanel != pauseMenuPanel) settingsPanel.SetActive(false);
|
||
|
||
SetupActionButtons();
|
||
SetupTabs();
|
||
InitializeSettingsUI();
|
||
|
||
wasCursorLocked = (Cursor.lockState == CursorLockMode.Locked);
|
||
}
|
||
|
||
private void Update()
|
||
{
|
||
// Не открываем меню паузы, если активен экран смерти
|
||
if (DeathScreenUI.Instance != null && DeathScreenUI.Instance.IsShown) return;
|
||
|
||
bool togglePause = false;
|
||
var keyboard = Keyboard.current;
|
||
if (keyboard != null)
|
||
{
|
||
if (keyboard.escapeKey.wasPressedThisFrame ||
|
||
keyboard.pKey.wasPressedThisFrame ||
|
||
keyboard.tabKey.wasPressedThisFrame ||
|
||
keyboard.pauseKey.wasPressedThisFrame)
|
||
{
|
||
togglePause = true;
|
||
}
|
||
}
|
||
|
||
var gamepad = Gamepad.current;
|
||
if (gamepad != null && gamepad.startButton.wasPressedThisFrame)
|
||
{
|
||
togglePause = true;
|
||
}
|
||
|
||
#if UNITY_EDITOR
|
||
// В Unity Editor при заблокированном курсоре нажатие Escape перехватывается редактором
|
||
// для снятия блокировки курсора (Cursor.lockState переключается в None) и не передается в Update().
|
||
// Поэтому в редакторе переключение lockState из Locked в None трактуется как нажатие Escape!
|
||
if (!isPaused && wasCursorLocked && Cursor.lockState != CursorLockMode.Locked)
|
||
{
|
||
togglePause = true;
|
||
}
|
||
wasCursorLocked = (Cursor.lockState == CursorLockMode.Locked);
|
||
#endif
|
||
|
||
if (togglePause)
|
||
{
|
||
if (isPaused)
|
||
{
|
||
if (settingsPanel != null && settingsPanel.activeSelf && settingsPanel != pauseMenuPanel)
|
||
{
|
||
CloseSettings();
|
||
}
|
||
else
|
||
{
|
||
ResumeGame();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
PauseGame();
|
||
}
|
||
}
|
||
}
|
||
|
||
public void PauseGame()
|
||
{
|
||
isPaused = true;
|
||
Time.timeScale = 0f;
|
||
Cursor.lockState = CursorLockMode.None;
|
||
Cursor.visible = true;
|
||
wasCursorLocked = false;
|
||
|
||
if (pauseMenuPanel != null)
|
||
{
|
||
pauseMenuPanel.SetActive(true);
|
||
SwitchTab(0);
|
||
}
|
||
|
||
if (settingsPanel != null && settingsPanel != pauseMenuPanel)
|
||
{
|
||
settingsPanel.SetActive(false);
|
||
}
|
||
|
||
Debug.Log("[PauseMenu] Game PAUSED (Esc/P/Tab pressed). Time.timeScale=0.");
|
||
}
|
||
|
||
public void ResumeGame()
|
||
{
|
||
isPaused = false;
|
||
Time.timeScale = 1f;
|
||
Cursor.lockState = CursorLockMode.Locked;
|
||
Cursor.visible = false;
|
||
wasCursorLocked = true;
|
||
|
||
if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false);
|
||
if (settingsPanel != null) settingsPanel.SetActive(false);
|
||
Debug.Log("[PauseMenu] Game RESUMED. Time.timeScale=1.");
|
||
}
|
||
|
||
public void RestartGame()
|
||
{
|
||
Time.timeScale = 1f;
|
||
Cursor.lockState = CursorLockMode.Locked;
|
||
Cursor.visible = false;
|
||
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
|
||
}
|
||
|
||
public void OpenSettings()
|
||
{
|
||
if (pauseMenuPanel != null) pauseMenuPanel.SetActive(false);
|
||
if (settingsPanel != null) settingsPanel.SetActive(true);
|
||
}
|
||
|
||
public void CloseSettings()
|
||
{
|
||
PlayerPrefs.Save();
|
||
if (settingsPanel != null) settingsPanel.SetActive(false);
|
||
if (pauseMenuPanel != null) pauseMenuPanel.SetActive(true);
|
||
}
|
||
|
||
public void ReturnToMainMenu()
|
||
{
|
||
Time.timeScale = 1f;
|
||
Cursor.lockState = CursorLockMode.None;
|
||
Cursor.visible = true;
|
||
SceneManager.LoadScene("MainMenu");
|
||
}
|
||
|
||
public void QuitGame()
|
||
{
|
||
PlayerPrefs.Save();
|
||
Application.Quit();
|
||
#if UNITY_EDITOR
|
||
UnityEditor.EditorApplication.isPlaying = false;
|
||
#endif
|
||
}
|
||
|
||
public void SwitchTab(int tabIndex)
|
||
{
|
||
if (tabPanels != null)
|
||
{
|
||
for (int i = 0; i < tabPanels.Length; i++)
|
||
{
|
||
if (tabPanels[i] != null)
|
||
{
|
||
tabPanels[i].SetActive(i == tabIndex);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (tabButtons != null)
|
||
{
|
||
for (int i = 0; i < tabButtons.Length; i++)
|
||
{
|
||
if (tabButtons[i] != null)
|
||
{
|
||
bool isActive = (i == tabIndex);
|
||
var img = tabButtons[i].GetComponent<Image>();
|
||
if (img != null)
|
||
{
|
||
img.color = isActive
|
||
? new Color(0.00f, 0.65f, 0.85f, 0.95f) // Неоновый бирюзовый акцент
|
||
: new Color(0.06f, 0.10f, 0.16f, 0.85f); // Темный фон неактивной вкладки
|
||
}
|
||
|
||
var text = tabButtons[i].GetComponentInChildren<Text>();
|
||
if (text != null)
|
||
{
|
||
text.color = isActive
|
||
? Color.white
|
||
: new Color(0.40f, 0.55f, 0.65f, 0.80f);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SetupTabs()
|
||
{
|
||
if (tabButtons != null)
|
||
{
|
||
for (int i = 0; i < tabButtons.Length; i++)
|
||
{
|
||
int index = i;
|
||
tabButtons[i].onClick.RemoveAllListeners();
|
||
tabButtons[i].onClick.AddListener(() => SwitchTab(index));
|
||
}
|
||
}
|
||
}
|
||
|
||
private void SetupActionButtons()
|
||
{
|
||
if (resumeButton != null)
|
||
{
|
||
resumeButton.onClick.RemoveAllListeners();
|
||
resumeButton.onClick.AddListener(ResumeGame);
|
||
}
|
||
|
||
if (restartButton != null)
|
||
{
|
||
restartButton.onClick.RemoveAllListeners();
|
||
restartButton.onClick.AddListener(RestartGame);
|
||
}
|
||
|
||
if (mainMenuButton != null)
|
||
{
|
||
mainMenuButton.onClick.RemoveAllListeners();
|
||
mainMenuButton.onClick.AddListener(ReturnToMainMenu);
|
||
}
|
||
|
||
if (quitButton != null)
|
||
{
|
||
quitButton.onClick.RemoveAllListeners();
|
||
quitButton.onClick.AddListener(QuitGame);
|
||
}
|
||
}
|
||
|
||
public void AutoWireUI()
|
||
{
|
||
if (pauseMenuPanel == null)
|
||
{
|
||
var root = transform.Find("PauseRootPanel");
|
||
if (root != null)
|
||
{
|
||
pauseMenuPanel = root.gameObject;
|
||
}
|
||
else
|
||
{
|
||
var panel = transform.Find("PauseMenuPanel");
|
||
if (panel != null) pauseMenuPanel = panel.gameObject;
|
||
}
|
||
}
|
||
|
||
Transform window = transform.Find("PauseRootPanel/Settings_Window");
|
||
if (window != null)
|
||
{
|
||
var winRt = window.GetComponent<RectTransform>();
|
||
if (winRt != null && (winRt.sizeDelta.x <= 0f || winRt.sizeDelta.y <= 0f))
|
||
{
|
||
winRt.anchorMin = new Vector2(0.5f, 0.5f);
|
||
winRt.anchorMax = new Vector2(0.5f, 0.5f);
|
||
winRt.pivot = new Vector2(0.5f, 0.5f);
|
||
winRt.anchoredPosition = Vector2.zero;
|
||
winRt.sizeDelta = new Vector2(1100f, 720f);
|
||
}
|
||
|
||
Transform dock = window.Find("Bottom_Action_Dock");
|
||
if (dock != null)
|
||
{
|
||
if (resumeButton == null) resumeButton = dock.Find("Btn_Resume")?.GetComponent<Button>();
|
||
if (restartButton == null) restartButton = dock.Find("Btn_Restart")?.GetComponent<Button>();
|
||
if (mainMenuButton == null) mainMenuButton = dock.Find("Btn_MainMenu")?.GetComponent<Button>();
|
||
if (quitButton == null) quitButton = dock.Find("Btn_Quit")?.GetComponent<Button>();
|
||
}
|
||
|
||
Transform tabBar = window.Find("Tab_Bar");
|
||
Transform content = window.Find("Content_Area");
|
||
|
||
if (tabBar != null && (tabButtons == null || tabButtons.Length == 0))
|
||
{
|
||
tabButtons = new Button[tabBar.childCount];
|
||
for (int i = 0; i < tabBar.childCount; i++)
|
||
{
|
||
tabButtons[i] = tabBar.GetChild(i).GetComponent<Button>();
|
||
}
|
||
}
|
||
|
||
if (content != null && (tabPanels == null || tabPanels.Length == 0))
|
||
{
|
||
tabPanels = new GameObject[]
|
||
{
|
||
content.Find("Panel_Audio")?.gameObject,
|
||
content.Find("Panel_Graphics")?.gameObject,
|
||
content.Find("Panel_Controls")?.gameObject,
|
||
content.Find("Panel_Briefing")?.gameObject
|
||
};
|
||
}
|
||
|
||
if (volumeSlider == null) volumeSlider = content?.Find("Panel_Audio/Row_Volume/Slider")?.GetComponent<Slider>();
|
||
if (volumeValueText == null) volumeValueText = content?.Find("Panel_Audio/Row_Volume/ValueText")?.GetComponent<Text>();
|
||
if (muteToggle == null) muteToggle = content?.Find("Panel_Audio/Row_Mute/Toggle")?.GetComponent<Toggle>();
|
||
|
||
if (qualityDropdown == null) qualityDropdown = content?.Find("Panel_Graphics/Row_Quality/Dropdown")?.GetComponent<Dropdown>();
|
||
if (fullscreenToggle == null) fullscreenToggle = content?.Find("Panel_Graphics/Row_Fullscreen/Toggle")?.GetComponent<Toggle>();
|
||
if (resolutionDropdown == null) resolutionDropdown = content?.Find("Panel_Graphics/Row_Resolution/Dropdown")?.GetComponent<Dropdown>();
|
||
if (vsyncToggle == null) vsyncToggle = content?.Find("Panel_Graphics/Row_Vsync/Toggle")?.GetComponent<Toggle>();
|
||
|
||
if (sensitivitySlider == null) sensitivitySlider = content?.Find("Panel_Controls/Row_Sensitivity/Slider")?.GetComponent<Slider>();
|
||
if (sensitivityValueText == null) sensitivityValueText = content?.Find("Panel_Controls/Row_Sensitivity/ValueText")?.GetComponent<Text>();
|
||
if (invertYToggle == null) invertYToggle = content?.Find("Panel_Controls/Row_InvertY/Toggle")?.GetComponent<Toggle>();
|
||
}
|
||
}
|
||
|
||
private void InitializeSettingsUI()
|
||
{
|
||
isInitializing = true;
|
||
|
||
// Звук
|
||
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.RemoveAllListeners();
|
||
volumeSlider.onValueChanged.AddListener(OnVolumeChanged);
|
||
}
|
||
|
||
if (muteToggle != null)
|
||
{
|
||
muteToggle.isOn = savedMute;
|
||
muteToggle.onValueChanged.RemoveAllListeners();
|
||
muteToggle.onValueChanged.AddListener(OnMuteChanged);
|
||
}
|
||
|
||
UpdateVolumeLabel(savedVolume, savedMute);
|
||
|
||
// Качество графики
|
||
if (qualityDropdown != null)
|
||
{
|
||
qualityDropdown.ClearOptions();
|
||
List<string> options = new List<string>();
|
||
foreach (var q in QualitySettings.names)
|
||
{
|
||
options.Add(TranslateQuality(q));
|
||
}
|
||
qualityDropdown.AddOptions(options);
|
||
qualityDropdown.value = PlayerPrefs.GetInt(GameSettings.KeyQualityLevel, QualitySettings.GetQualityLevel());
|
||
qualityDropdown.RefreshShownValue();
|
||
qualityDropdown.onValueChanged.RemoveAllListeners();
|
||
qualityDropdown.onValueChanged.AddListener(OnQualityChanged);
|
||
}
|
||
|
||
// Полноэкранный режим
|
||
if (fullscreenToggle != null)
|
||
{
|
||
fullscreenToggle.isOn = PlayerPrefs.GetInt(GameSettings.KeyFullscreen, Screen.fullScreen ? 1 : 0) == 1;
|
||
fullscreenToggle.onValueChanged.RemoveAllListeners();
|
||
fullscreenToggle.onValueChanged.AddListener(OnFullscreenChanged);
|
||
}
|
||
|
||
// Разрешения экрана
|
||
PopulateResolutions();
|
||
|
||
// Вертикальная синхронизация
|
||
if (vsyncToggle != null)
|
||
{
|
||
vsyncToggle.isOn = QualitySettings.vSyncCount > 0;
|
||
vsyncToggle.onValueChanged.RemoveAllListeners();
|
||
vsyncToggle.onValueChanged.AddListener(OnVsyncChanged);
|
||
}
|
||
|
||
// Чувствительность
|
||
float savedSens = PlayerPrefs.GetFloat(GameSettings.KeyMouseSensitivity, 1.0f);
|
||
if (sensitivitySlider != null)
|
||
{
|
||
sensitivitySlider.minValue = 0.2f;
|
||
sensitivitySlider.maxValue = 3.0f;
|
||
sensitivitySlider.value = savedSens;
|
||
sensitivitySlider.onValueChanged.RemoveAllListeners();
|
||
sensitivitySlider.onValueChanged.AddListener(OnSensitivityChanged);
|
||
}
|
||
|
||
UpdateSensitivityLabel(savedSens);
|
||
|
||
// Инверсия по Y
|
||
if (invertYToggle != null)
|
||
{
|
||
invertYToggle.isOn = PlayerPrefs.GetInt("Settings_InvertY", 0) == 1;
|
||
invertYToggle.onValueChanged.RemoveAllListeners();
|
||
invertYToggle.onValueChanged.AddListener(OnInvertYChanged);
|
||
}
|
||
|
||
isInitializing = false;
|
||
}
|
||
|
||
private void PopulateResolutions()
|
||
{
|
||
if (resolutionDropdown == null) return;
|
||
|
||
resolutionDropdown.ClearOptions();
|
||
var allRes = Screen.resolutions;
|
||
filteredResolutions.Clear();
|
||
var options = new List<string>();
|
||
var seen = new HashSet<string>();
|
||
|
||
for (int i = allRes.Length - 1; i >= 0; i--)
|
||
{
|
||
var res = allRes[i];
|
||
string key = $"{res.width}x{res.height}";
|
||
if (!seen.Contains(key))
|
||
{
|
||
seen.Add(key);
|
||
filteredResolutions.Insert(0, res);
|
||
}
|
||
}
|
||
|
||
if (filteredResolutions.Count == 0)
|
||
{
|
||
filteredResolutions.Add(new Resolution { width = 1920, height = 1080 });
|
||
filteredResolutions.Add(new Resolution { width = 1280, height = 720 });
|
||
}
|
||
|
||
int currentIdx = 0;
|
||
int curW = Screen.width;
|
||
int curH = Screen.height;
|
||
|
||
for (int i = 0; i < filteredResolutions.Count; i++)
|
||
{
|
||
var r = filteredResolutions[i];
|
||
options.Add($"{r.width} x {r.height}");
|
||
if (r.width == curW && r.height == curH)
|
||
{
|
||
currentIdx = i;
|
||
}
|
||
}
|
||
|
||
resolutionDropdown.AddOptions(options);
|
||
resolutionDropdown.value = currentIdx;
|
||
resolutionDropdown.RefreshShownValue();
|
||
resolutionDropdown.onValueChanged.RemoveAllListeners();
|
||
resolutionDropdown.onValueChanged.AddListener(OnResolutionChanged);
|
||
}
|
||
|
||
private void OnResolutionChanged(int index)
|
||
{
|
||
if (isInitializing || index < 0 || index >= filteredResolutions.Count) return;
|
||
var r = filteredResolutions[index];
|
||
bool isFull = fullscreenToggle != null ? fullscreenToggle.isOn : Screen.fullScreen;
|
||
Screen.SetResolution(r.width, r.height, isFull);
|
||
PlayerPrefs.SetInt(GameSettings.KeyResolutionWidth, r.width);
|
||
PlayerPrefs.SetInt(GameSettings.KeyResolutionHeight, r.height);
|
||
}
|
||
|
||
private void OnVsyncChanged(bool enabled)
|
||
{
|
||
if (isInitializing) return;
|
||
QualitySettings.vSyncCount = enabled ? 1 : 0;
|
||
}
|
||
|
||
private void OnInvertYChanged(bool enabled)
|
||
{
|
||
if (isInitializing) return;
|
||
PlayerPrefs.SetInt("Settings_InvertY", enabled ? 1 : 0);
|
||
}
|
||
|
||
private 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);
|
||
}
|
||
|
||
private 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 vol, bool muted)
|
||
{
|
||
if (volumeValueText != null)
|
||
{
|
||
volumeValueText.text = muted ? "ВЫКЛ" : $"{Mathf.RoundToInt(vol * 100)}%";
|
||
}
|
||
}
|
||
|
||
private void OnQualityChanged(int index)
|
||
{
|
||
if (isInitializing) return;
|
||
QualitySettings.SetQualityLevel(index, true);
|
||
PlayerPrefs.SetInt(GameSettings.KeyQualityLevel, index);
|
||
}
|
||
|
||
private void OnFullscreenChanged(bool isFullscreen)
|
||
{
|
||
if (isInitializing) return;
|
||
Screen.fullScreen = isFullscreen;
|
||
PlayerPrefs.SetInt(GameSettings.KeyFullscreen, isFullscreen ? 1 : 0);
|
||
}
|
||
|
||
private 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";
|
||
}
|
||
}
|
||
|
||
private string TranslateQuality(string q)
|
||
{
|
||
switch (q.ToLower())
|
||
{
|
||
case "very low": return "Очень низкое";
|
||
case "low": return "Низкое";
|
||
case "medium": return "Среднее";
|
||
case "high": return "Высокое";
|
||
case "very high": return "Очень высокое";
|
||
case "ultra": return "Ультра";
|
||
default: return q;
|
||
}
|
||
}
|
||
}
|