using System.Collections.Generic; using UnityEngine; using UnityEngine.Audio; using UnityEngine.Events; using Ashwild.Core; namespace Ashwild.Settings { public class SettingsManager : MonoBehaviour { public static SettingsManager Instance { get; private set; } [Header("Audio Mixer")] [SerializeField] private AudioMixer audioMixer; [SerializeField] private string masterParam = "MasterVol"; [SerializeField] private string musicParam = "MusicVol"; [SerializeField] private string sfxParam = "SfxVol"; [Header("Events")] public UnityEvent onSettingsChanged; // ── Display ───────────────────────────────────────────────── public int ResolutionIndex { get; private set; } public FullScreenMode FullscreenMode { get; private set; } public int MonitorIndex { get; private set; } public float Brightness { get; private set; } // ── Audio ─────────────────────────────────────────────────── public float MasterVolume { get; private set; } public float MusicVolume { get; private set; } public float SfxVolume { get; private set; } // ── Graphics ──────────────────────────────────────────────── public int QualityLevel { get; private set; } public bool VSync { get; private set; } public int TargetFps { get; private set; } public float Fov { get; private set; } // ── Controls ──────────────────────────────────────────────── public float Sensitivity { get; private set; } public bool InvertY { get; private set; } public bool CrosshairEnabled { get; private set; } // ── PlayerPrefs keys ──────────────────────────────────────── private const string K_RESOLUTION = "settings.display.resolutionIndex"; private const string K_FULLSCREEN = "settings.display.fullscreenMode"; private const string K_MONITOR = "settings.display.monitorIndex"; private const string K_BRIGHTNESS = "settings.display.brightness"; private const string K_MASTER = "settings.audio.master"; private const string K_MUSIC = "settings.audio.music"; private const string K_SFX = "settings.audio.sfx"; private const string K_QUALITY = "settings.graphics.qualityLevel"; private const string K_VSYNC = "settings.graphics.vsync"; private const string K_TARGET_FPS = "settings.graphics.targetFps"; private const string K_FOV = "settings.graphics.fov"; private const string K_SENS = "settings.controls.sensitivity"; private const string K_INVERT_Y = "settings.controls.invertY"; private const string K_CROSSHAIR = "settings.controls.crosshair"; private void Awake() { if (Instance != null && Instance != this) { Destroy(gameObject); return; } Instance = this; Persistence.Persist(gameObject); LoadAll(); ApplyAll(); } // ── Public setters ────────────────────────────────────────── public void SetResolution(int index) { Resolution[] resolutions = Screen.resolutions; if (index < 0 || index >= resolutions.Length) return; ResolutionIndex = index; Resolution r = resolutions[index]; Screen.SetResolution(r.width, r.height, FullscreenMode, r.refreshRateRatio); Save(); onSettingsChanged?.Invoke(); } public void SetFullscreenMode(FullScreenMode mode) { FullscreenMode = mode; Screen.fullScreenMode = mode; Save(); onSettingsChanged?.Invoke(); } public void SetMonitor(int index) { List displays = new List(); Screen.GetDisplayLayout(displays); if (index < 0 || index >= displays.Count) return; MonitorIndex = index; Screen.MoveMainWindowTo(displays[index], Vector2Int.zero); Save(); onSettingsChanged?.Invoke(); } public void SetBrightness(float value) { Brightness = Mathf.Clamp(value, 0.5f, 1.5f); Save(); onSettingsChanged?.Invoke(); } public void SetMasterVolume(float value) { MasterVolume = Mathf.Clamp01(value); ApplyMasterVolume(); Save(); onSettingsChanged?.Invoke(); } public void SetMusicVolume(float value) { MusicVolume = Mathf.Clamp01(value); ApplyMusicVolume(); Save(); onSettingsChanged?.Invoke(); } public void SetSfxVolume(float value) { SfxVolume = Mathf.Clamp01(value); ApplySfxVolume(); Save(); onSettingsChanged?.Invoke(); } public void SetQualityLevel(int level) { QualityLevel = Mathf.Clamp(level, 0, QualitySettings.names.Length - 1); QualitySettings.SetQualityLevel(QualityLevel, true); Save(); onSettingsChanged?.Invoke(); } public void SetVSync(bool enabled) { VSync = enabled; QualitySettings.vSyncCount = enabled ? 1 : 0; Save(); onSettingsChanged?.Invoke(); } public void SetTargetFps(int fps) { TargetFps = Mathf.Clamp(fps, 30, 360); Application.targetFrameRate = TargetFps; Save(); onSettingsChanged?.Invoke(); } public void SetFov(float fov) { Fov = Mathf.Clamp(fov, 60f, 110f); Save(); onSettingsChanged?.Invoke(); } public void SetSensitivity(float value) { Sensitivity = Mathf.Clamp(value, 0.05f, 2f); Save(); onSettingsChanged?.Invoke(); } public void SetInvertY(bool enabled) { InvertY = enabled; Save(); onSettingsChanged?.Invoke(); } public void SetCrosshairEnabled(bool enabled) { CrosshairEnabled = enabled; Save(); onSettingsChanged?.Invoke(); } // ── Load / Save / Apply ───────────────────────────────────── private void LoadAll() { int defaultResolution = Mathf.Max(0, Screen.resolutions.Length - 1); ResolutionIndex = PlayerPrefs.GetInt(K_RESOLUTION, defaultResolution); FullscreenMode = (FullScreenMode)PlayerPrefs.GetInt(K_FULLSCREEN, (int)FullScreenMode.FullScreenWindow); MonitorIndex = PlayerPrefs.GetInt(K_MONITOR, 0); Brightness = PlayerPrefs.GetFloat(K_BRIGHTNESS, 1f); MasterVolume = PlayerPrefs.GetFloat(K_MASTER, 1f); MusicVolume = PlayerPrefs.GetFloat(K_MUSIC, 0.8f); SfxVolume = PlayerPrefs.GetFloat(K_SFX, 1f); QualityLevel = PlayerPrefs.GetInt(K_QUALITY, QualitySettings.GetQualityLevel()); VSync = PlayerPrefs.GetInt(K_VSYNC, 1) == 1; TargetFps = PlayerPrefs.GetInt(K_TARGET_FPS, 144); Fov = PlayerPrefs.GetFloat(K_FOV, 75f); Sensitivity = PlayerPrefs.GetFloat(K_SENS, 0.15f); InvertY = PlayerPrefs.GetInt(K_INVERT_Y, 0) == 1; CrosshairEnabled = PlayerPrefs.GetInt(K_CROSSHAIR, 1) == 1; } private void Save() { PlayerPrefs.SetInt(K_RESOLUTION, ResolutionIndex); PlayerPrefs.SetInt(K_FULLSCREEN, (int)FullscreenMode); PlayerPrefs.SetInt(K_MONITOR, MonitorIndex); PlayerPrefs.SetFloat(K_BRIGHTNESS, Brightness); PlayerPrefs.SetFloat(K_MASTER, MasterVolume); PlayerPrefs.SetFloat(K_MUSIC, MusicVolume); PlayerPrefs.SetFloat(K_SFX, SfxVolume); PlayerPrefs.SetInt(K_QUALITY, QualityLevel); PlayerPrefs.SetInt(K_VSYNC, VSync ? 1 : 0); PlayerPrefs.SetInt(K_TARGET_FPS, TargetFps); PlayerPrefs.SetFloat(K_FOV, Fov); PlayerPrefs.SetFloat(K_SENS, Sensitivity); PlayerPrefs.SetInt(K_INVERT_Y, InvertY ? 1 : 0); PlayerPrefs.SetInt(K_CROSSHAIR, CrosshairEnabled ? 1 : 0); PlayerPrefs.Save(); } private void ApplyAll() { // Display Resolution[] resolutions = Screen.resolutions; if (ResolutionIndex >= 0 && ResolutionIndex < resolutions.Length) { Resolution r = resolutions[ResolutionIndex]; Screen.SetResolution(r.width, r.height, FullscreenMode, r.refreshRateRatio); } if (MonitorIndex > 0) { List displays = new List(); Screen.GetDisplayLayout(displays); if (MonitorIndex < displays.Count) Screen.MoveMainWindowTo(displays[MonitorIndex], Vector2Int.zero); } // Audio ApplyMasterVolume(); ApplyMusicVolume(); ApplySfxVolume(); // Graphics QualitySettings.SetQualityLevel(QualityLevel, true); QualitySettings.vSyncCount = VSync ? 1 : 0; Application.targetFrameRate = TargetFps; } // ── Audio helpers ─────────────────────────────────────────── private void ApplyMasterVolume() => SetMixer(masterParam, MasterVolume); private void ApplyMusicVolume() => SetMixer(musicParam, MusicVolume); private void ApplySfxVolume() => SetMixer(sfxParam, SfxVolume); private void SetMixer(string param, float linear) { if (audioMixer == null || string.IsNullOrEmpty(param)) return; audioMixer.SetFloat(param, Mathf.Log10(Mathf.Max(linear, 0.0001f)) * 20f); } } }