Files
Echo/Assets/_Project/Scripts/UI/Settings/SettingsController.cs
T
2026-07-08 20:42:51 +08:00

755 lines
31 KiB
C#

using System.Collections;
using System.Linq;
using Core.SettingsSystem;
using Michsky.UI.Reach;
using UnityEngine;
using UI.PanelStack;
using Core.InputLock;
namespace UI.Settings
{
public class SettingsController : MonoBehaviour
{
private struct ResolutionOption
{
public int width;
public int height;
public int refreshRate;
}
[SerializeField] private bool debugLogs = true;
[Header("Root")]
[SerializeField] private GameObject rootObject;
[SerializeField] private PanelManager panelManager;
[Header("Hotkeys")]
[SerializeField] private HotkeyEvent escapeHotkey;
[Header("Buttons (Optional)")]
[SerializeField] private ButtonManager backButton;
[SerializeField] private ButtonManager applyButton;
[SerializeField] private ButtonManager resetButton;
[Header("Graphics - Dropdowns")]
[SerializeField] private Dropdown resolutionDropdown;
[SerializeField] private Dropdown displayModeDropdown;
[SerializeField] private Dropdown qualityDropdown;
[SerializeField] private Dropdown vSyncDropdown;
[SerializeField] private Dropdown msaaDropdown;
[SerializeField] private Dropdown textureQualityDropdown;
[Header("Graphics - Sliders")]
[SerializeField] private SliderManager targetFpsSlider;
[SerializeField] private SliderManager renderScaleSlider;
[SerializeField] private SliderManager shadowDistanceSlider;
[Header("Audio - Sliders")]
[SerializeField] private SliderManager masterVolumeSlider;
[SerializeField] private SliderManager musicVolumeSlider;
[SerializeField] private SliderManager sfxVolumeSlider;
[Header("Gameplay - Sliders")]
[SerializeField] private SliderManager mouseSensitivitySlider;
[Header("Gameplay - Dropdowns")]
[SerializeField] private Dropdown llmProviderDropdown;
[Header("Auto Initialize")]
[SerializeField] private bool initResolutionDropdown = true;
[SerializeField] private bool initQualityDropdown = true;
[SerializeField] private bool delayRefreshOneFrameOnEnable = true;
[SerializeField] private bool startHidden = true;
[Header("Dropdown Item Source")]
[SerializeField] private bool rebuildResolutionItems = true;
[SerializeField] private bool rebuildDisplayModeItems = true;
[SerializeField] private bool rebuildQualityItems = true;
[SerializeField] private bool rebuildVSyncItems = true;
[SerializeField] private bool rebuildMsaaItems = true;
[SerializeField] private bool rebuildTextureQualityItems = true;
[SerializeField] private bool rebuildLlmProviderItems = true;
[Header("Ranges")]
[SerializeField] private Vector2Int targetFpsRange = new Vector2Int(1, 240);
[SerializeField] private Vector2 renderScalePercentRange = new Vector2(0.1f, 200f);
[SerializeField] private Vector2 shadowDistanceRange = new Vector2(0f, 150f);
[SerializeField] private Vector2 sensitivityRange = new Vector2(0.1f, 10f);
private SettingsData applied;
private SettingsData working;
private bool isDirty;
private bool isWired;
private bool isPopulating;
private bool isClosing;
private ResolutionOption[] resolutionOptions = System.Array.Empty<ResolutionOption>();
private float RenderScalePercentMin => Mathf.Max(renderScalePercentRange.x, 10f);
private float RenderScalePercentMax => Mathf.Max(RenderScalePercentMin, renderScalePercentRange.y);
private void Awake()
{
WireOnce();
if (startHidden)
{
ShowRoot(false);
}
if (debugLogs) Debug.Log($"[SettingsController] Awake. startHidden={startHidden} rootActive={(rootObject!=null?rootObject.activeSelf:gameObject.activeSelf)}");
}
private void OnEnable()
{
WireOnce();
ShowRoot(true);
if (delayRefreshOneFrameOnEnable) { StartCoroutine(RefreshNextFrame()); }
else { RefreshFromApplied(); }
}
private IEnumerator RefreshNextFrame()
{
yield return null;
RefreshFromApplied();
}
public void Show()
{
RefreshFromApplied();
ShowRoot(true);
if (panelManager != null) { panelManager.ShowCurrentPanel(); }
}
public void SetResolutionIndex(int index) { OnResolutionChanged(index); }
public void SetDisplayModeIndex(int index) { OnDisplayModeChanged(index); }
public void SetQualityIndex(int index) { OnQualityChanged(index); }
public void SetVSyncIndex(int index) { OnVSyncChanged(index); }
public void SetMsaaIndex(int index) { OnMsaaChanged(index); }
public void SetTextureQualityIndex(int index) { OnTextureQualityChanged(index); }
public void SetTargetFps(float value) { OnTargetFpsChanged(value); }
public void SetRenderScalePercent(float value) { OnRenderScalePercentChanged(value); }
public void SetShadowDistance(float value) { OnShadowDistanceChanged(value); }
public void SetMasterVolume(float value) { OnMasterChanged(value); }
public void SetMusicVolume(float value) { OnMusicChanged(value); }
public void SetSfxVolume(float value) { OnSfxChanged(value); }
public void SetMouseSensitivity(float value) { OnSensitivityChanged(value); }
public void SetLLMProviderIndex(int index) { OnLLMProviderChanged(index); }
public void CloseOrBack()
{
if (isClosing) { return; }
isClosing = true;
if (debugLogs) Debug.Log($"[SettingsController] CloseOrBack called. panelManagerActive={(panelManager!=null && panelManager.gameObject.activeInHierarchy)}");
if (isDirty)
{
working = Clone(applied);
PopulateUI(working);
isDirty = false;
}
if (UIPanelStack.Instance != null)
{
UIPanelStack.Instance.Remove(rootObject != null ? rootObject : gameObject);
}
if (PlayerControlLockService.Instance != null)
{
PlayerControlLockService.Instance.Unlock(this);
}
float delay = 0f;
if (panelManager != null && panelManager.isActiveAndEnabled && panelManager.gameObject.activeInHierarchy)
{
panelManager.HideCurrentPanel();
delay = panelManager.cachedStateLength * panelManager.animationSpeed;
}
if (rootObject != null) { StartCoroutine(HideAfter(delay)); }
else { StartCoroutine(HideAfter(delay)); }
}
public void OpenAtIndex(int index)
{
isClosing = false;
if (debugLogs) Debug.Log($"[SettingsController] OpenAtIndex({index}) called. panelManager={(panelManager!=null)}");
ShowRoot(true);
if (panelManager != null && !panelManager.gameObject.activeInHierarchy)
{
panelManager.gameObject.SetActive(true);
}
if (panelManager != null) { panelManager.OpenPanelByIndex(index); }
if (delayRefreshOneFrameOnEnable) { StartCoroutine(RefreshNextFrame()); }
else { RefreshFromApplied(); }
if (UIPanelStack.Instance != null) { UIPanelStack.Instance.Push(UIPanelKind.Settings, rootObject != null ? rootObject : gameObject, CloseOrBack); }
if (PlayerControlLockService.Instance != null)
{
PlayerControlLockService.Instance.Lock(this);
}
}
public void Apply()
{
SettingsService service = SettingsService.Instance;
if (service == null) { return; }
if (debugLogs) Debug.Log($"[SettingsController] Apply clicked. gameplay.mouseSensitivity={working?.gameplay?.mouseSensitivity}");
service.ApplyAndSave(Clone(working));
applied = Clone(service.Current);
isDirty = false;
}
public void ResetToDefaults()
{
working = SettingsData.Default();
PopulateUI(working);
isDirty = true;
}
private IEnumerator HideAfter(float seconds)
{
if (seconds > 0f) { yield return new WaitForSecondsRealtime(seconds); }
ShowRoot(false);
isClosing = false;
}
private void ShowRoot(bool show)
{
if (rootObject != null) { rootObject.SetActive(show); }
else { gameObject.SetActive(show); }
if (debugLogs) Debug.Log($"[SettingsController] ShowRoot({show}).");
}
private void RefreshFromApplied()
{
SettingsService service = SettingsService.Instance;
applied = service != null ? Clone(service.Current) : SettingsData.Default();
working = Clone(applied);
EnsureDropdownItems();
ApplyRanges();
PopulateUI(working);
isDirty = false;
}
private void WireOnce()
{
if (isWired) { return; }
if (escapeHotkey != null) { escapeHotkey.onHotkeyPress.AddListener(CloseOrBack); }
if (backButton != null) { backButton.onClick.AddListener(CloseOrBack); }
if (applyButton != null) { applyButton.onClick.AddListener(Apply); }
if (resetButton != null) { resetButton.onClick.AddListener(ResetToDefaults); }
if (resolutionDropdown != null) { resolutionDropdown.onValueChanged.AddListener(OnResolutionChanged); }
if (displayModeDropdown != null) { displayModeDropdown.onValueChanged.AddListener(OnDisplayModeChanged); }
if (qualityDropdown != null) { qualityDropdown.onValueChanged.AddListener(OnQualityChanged); }
if (vSyncDropdown != null) { vSyncDropdown.onValueChanged.AddListener(OnVSyncChanged); }
if (msaaDropdown != null) { msaaDropdown.onValueChanged.AddListener(OnMsaaChanged); }
if (textureQualityDropdown != null) { textureQualityDropdown.onValueChanged.AddListener(OnTextureQualityChanged); }
if (targetFpsSlider != null) { targetFpsSlider.onValueChanged.AddListener(OnTargetFpsChanged); }
if (renderScaleSlider != null) { renderScaleSlider.onValueChanged.AddListener(OnRenderScalePercentChanged); }
if (shadowDistanceSlider != null) { shadowDistanceSlider.onValueChanged.AddListener(OnShadowDistanceChanged); }
if (masterVolumeSlider != null) { masterVolumeSlider.onValueChanged.AddListener(OnMasterChanged); }
if (musicVolumeSlider != null) { musicVolumeSlider.onValueChanged.AddListener(OnMusicChanged); }
if (sfxVolumeSlider != null) { sfxVolumeSlider.onValueChanged.AddListener(OnSfxChanged); }
if (mouseSensitivitySlider != null) { mouseSensitivitySlider.onValueChanged.AddListener(OnSensitivityChanged); }
if (llmProviderDropdown != null) { llmProviderDropdown.onValueChanged.AddListener(OnLLMProviderChanged); }
isWired = true;
}
private void EnsureDropdownItems()
{
if (initResolutionDropdown && resolutionDropdown != null && (rebuildResolutionItems || resolutionDropdown.items == null || resolutionDropdown.items.Count == 0))
{
resolutionDropdown.items.Clear();
var resolutions = Screen.resolutions;
int currentWidth = Screen.width;
int currentHeight = Screen.height;
#if UNITY_2022_2_OR_NEWER
int currentRefresh = Mathf.RoundToInt((float)Screen.currentResolution.refreshRateRatio.value);
#else
int currentRefresh = Screen.currentResolution.refreshRate;
#endif
var grouped = resolutions
.GroupBy(r => new Vector2Int(r.width, r.height))
.Select(g =>
{
var list = g.ToArray();
int bestIndex = 0;
int bestRefresh = -1;
for (int i = 0; i < list.Length; i++)
{
#if UNITY_2022_2_OR_NEWER
int rr = Mathf.RoundToInt((float)list[i].refreshRateRatio.value);
#else
int rr = list[i].refreshRate;
#endif
bool prefer = rr == currentRefresh;
if (prefer)
{
bestIndex = i;
bestRefresh = rr;
break;
}
if (rr > bestRefresh)
{
bestIndex = i;
bestRefresh = rr;
}
}
var chosen = list[bestIndex];
return new ResolutionOption
{
width = chosen.width,
height = chosen.height,
refreshRate = Mathf.Max(0, bestRefresh)
};
})
.OrderBy(o => o.width)
.ThenBy(o => o.height)
.ToArray();
resolutionOptions = grouped;
int currentIndex = 0;
for (int i = 0; i < resolutionOptions.Length; i++)
{
var opt = resolutionOptions[i];
string label = opt.refreshRate > 0 ? $"{opt.width}x{opt.height} @ {opt.refreshRate}Hz" : $"{opt.width}x{opt.height}";
resolutionDropdown.CreateNewItem(label, false);
if (opt.width == currentWidth && opt.height == currentHeight) { currentIndex = i; }
}
resolutionDropdown.selectedItemIndex = Mathf.Clamp(currentIndex, 0, Mathf.Max(0, resolutionDropdown.items.Count - 1));
resolutionDropdown.Initialize();
}
else if (resolutionDropdown != null)
{
EnsureDropdownInitialized(resolutionDropdown);
}
if (displayModeDropdown != null && (rebuildDisplayModeItems || displayModeDropdown.items == null || displayModeDropdown.items.Count == 0))
{
displayModeDropdown.items.Clear();
displayModeDropdown.CreateNewItem("Fullscreen", false);
displayModeDropdown.CreateNewItem("Borderless", false);
displayModeDropdown.CreateNewItem("Windowed", false);
displayModeDropdown.Initialize();
}
else if (displayModeDropdown != null)
{
EnsureDropdownInitialized(displayModeDropdown);
}
if (initQualityDropdown && qualityDropdown != null && (rebuildQualityItems || qualityDropdown.items == null || qualityDropdown.items.Count == 0))
{
qualityDropdown.items.Clear();
foreach (string q in QualitySettings.names) { qualityDropdown.CreateNewItem(q, false); }
qualityDropdown.Initialize();
}
else if (qualityDropdown != null)
{
EnsureDropdownInitialized(qualityDropdown);
}
if (vSyncDropdown != null && (rebuildVSyncItems || vSyncDropdown.items == null || vSyncDropdown.items.Count == 0))
{
vSyncDropdown.items.Clear();
vSyncDropdown.CreateNewItem("Off", false);
vSyncDropdown.CreateNewItem("On", false);
vSyncDropdown.Initialize();
}
else if (vSyncDropdown != null)
{
EnsureDropdownInitialized(vSyncDropdown);
}
if (msaaDropdown != null && (rebuildMsaaItems || msaaDropdown.items == null || msaaDropdown.items.Count == 0))
{
msaaDropdown.items.Clear();
msaaDropdown.CreateNewItem("Off", false);
msaaDropdown.CreateNewItem("2x", false);
msaaDropdown.CreateNewItem("4x", false);
msaaDropdown.CreateNewItem("8x", false);
msaaDropdown.Initialize();
}
else if (msaaDropdown != null)
{
EnsureDropdownInitialized(msaaDropdown);
}
if (textureQualityDropdown != null && (rebuildTextureQualityItems || textureQualityDropdown.items == null || textureQualityDropdown.items.Count == 0))
{
textureQualityDropdown.items.Clear();
textureQualityDropdown.CreateNewItem("Full", false);
textureQualityDropdown.CreateNewItem("Half", false);
textureQualityDropdown.CreateNewItem("Quarter", false);
textureQualityDropdown.CreateNewItem("Eighth", false);
textureQualityDropdown.Initialize();
}
else if (textureQualityDropdown != null)
{
EnsureDropdownInitialized(textureQualityDropdown);
}
if (llmProviderDropdown != null && (rebuildLlmProviderItems || llmProviderDropdown.items == null || llmProviderDropdown.items.Count == 0))
{
llmProviderDropdown.items.Clear();
llmProviderDropdown.CreateNewItem("DeepSeek", false);
llmProviderDropdown.CreateNewItem("Doubao", false);
llmProviderDropdown.Initialize();
}
else if (llmProviderDropdown != null)
{
EnsureDropdownInitialized(llmProviderDropdown);
}
}
private void EnsureDropdownInitialized(Dropdown dropdown)
{
if (dropdown.items == null || dropdown.items.Count == 0) { return; }
for (int i = 0; i < dropdown.items.Count; i++)
{
if (dropdown.items[i].itemButton == null)
{
dropdown.Initialize();
break;
}
}
}
private void ApplyRanges()
{
if (targetFpsSlider != null && targetFpsSlider.mainSlider != null)
{
targetFpsSlider.mainSlider.minValue = targetFpsRange.x;
targetFpsSlider.mainSlider.maxValue = targetFpsRange.y;
targetFpsSlider.useRoundValue = true;
}
if (renderScaleSlider != null && renderScaleSlider.mainSlider != null)
{
renderScaleSlider.mainSlider.minValue = RenderScalePercentMin;
renderScaleSlider.mainSlider.maxValue = RenderScalePercentMax;
renderScaleSlider.usePercent = true;
renderScaleSlider.useRoundValue = true;
}
if (shadowDistanceSlider != null && shadowDistanceSlider.mainSlider != null)
{
shadowDistanceSlider.mainSlider.minValue = shadowDistanceRange.x;
shadowDistanceSlider.mainSlider.maxValue = shadowDistanceRange.y;
shadowDistanceSlider.useRoundValue = false;
}
if (masterVolumeSlider != null && masterVolumeSlider.mainSlider != null)
{
masterVolumeSlider.mainSlider.minValue = 0f;
masterVolumeSlider.mainSlider.maxValue = 1f;
masterVolumeSlider.usePercent = true;
}
if (musicVolumeSlider != null && musicVolumeSlider.mainSlider != null)
{
musicVolumeSlider.mainSlider.minValue = 0f;
musicVolumeSlider.mainSlider.maxValue = 1f;
musicVolumeSlider.usePercent = true;
}
if (sfxVolumeSlider != null && sfxVolumeSlider.mainSlider != null)
{
sfxVolumeSlider.mainSlider.minValue = 0f;
sfxVolumeSlider.mainSlider.maxValue = 1f;
sfxVolumeSlider.usePercent = true;
}
if (mouseSensitivitySlider != null && mouseSensitivitySlider.mainSlider != null)
{
mouseSensitivitySlider.mainSlider.minValue = sensitivityRange.x;
mouseSensitivitySlider.mainSlider.maxValue = sensitivityRange.y;
mouseSensitivitySlider.useRoundValue = false;
}
}
private void PopulateUI(SettingsData data)
{
if (data == null) { return; }
isPopulating = true;
if (resolutionDropdown != null)
{
int idx = -1;
if (data.graphics.resolutionWidth > 0 && data.graphics.resolutionHeight > 0 && resolutionOptions.Length > 0)
{
int best = -1;
int bestRefreshDiff = int.MaxValue;
for (int i = 0; i < resolutionOptions.Length; i++)
{
var opt = resolutionOptions[i];
if (opt.width != data.graphics.resolutionWidth || opt.height != data.graphics.resolutionHeight) { continue; }
if (data.graphics.resolutionRefreshRate > 0 && opt.refreshRate > 0)
{
int diff = Mathf.Abs(opt.refreshRate - data.graphics.resolutionRefreshRate);
if (diff < bestRefreshDiff) { bestRefreshDiff = diff; best = i; }
}
else
{
best = i;
break;
}
}
idx = best;
}
else if (data.graphics.resolutionIndex >= 0)
{
var resolutions = Screen.resolutions;
if (data.graphics.resolutionIndex >= 0 && data.graphics.resolutionIndex < resolutions.Length && resolutionOptions.Length > 0)
{
var r = resolutions[data.graphics.resolutionIndex];
for (int i = 0; i < resolutionOptions.Length; i++)
{
if (resolutionOptions[i].width == r.width && resolutionOptions[i].height == r.height) { idx = i; break; }
}
}
}
if (idx < 0) { idx = resolutionDropdown.selectedItemIndex; }
idx = Mathf.Clamp(idx, 0, Mathf.Max(0, resolutionDropdown.items.Count - 1));
EnsureDropdownInitialized(resolutionDropdown);
if (resolutionDropdown.items != null && resolutionDropdown.items.Count > 0) { resolutionDropdown.SetDropdownIndex(idx); }
}
if (displayModeDropdown != null)
{
int modeIndex = FullscreenModeToIndex(data.graphics.fullscreenMode);
EnsureDropdownInitialized(displayModeDropdown);
if (displayModeDropdown.items != null && displayModeDropdown.items.Count > 0) { displayModeDropdown.SetDropdownIndex(modeIndex); }
}
if (qualityDropdown != null)
{
int q = data.graphics.qualityLevel;
if (q < 0) { q = QualitySettings.GetQualityLevel(); }
q = Mathf.Clamp(q, 0, Mathf.Max(0, qualityDropdown.items.Count - 1));
EnsureDropdownInitialized(qualityDropdown);
if (qualityDropdown.items != null && qualityDropdown.items.Count > 0) { qualityDropdown.SetDropdownIndex(q); }
}
if (vSyncDropdown != null)
{
EnsureDropdownInitialized(vSyncDropdown);
if (vSyncDropdown.items != null && vSyncDropdown.items.Count > 0) { vSyncDropdown.SetDropdownIndex(data.graphics.vSync ? 1 : 0); }
}
if (msaaDropdown != null)
{
EnsureDropdownInitialized(msaaDropdown);
if (msaaDropdown.items != null && msaaDropdown.items.Count > 0) { msaaDropdown.SetDropdownIndex(MsaaToIndex(data.graphics.msaaSampleCount)); }
}
if (textureQualityDropdown != null)
{
int t = Mathf.Clamp(data.graphics.textureMipmapLimit, 0, 3);
EnsureDropdownInitialized(textureQualityDropdown);
if (textureQualityDropdown.items != null && textureQualityDropdown.items.Count > 0) { textureQualityDropdown.SetDropdownIndex(t); }
}
if (targetFpsSlider != null && targetFpsSlider.mainSlider != null)
{
float v = data.graphics.targetFps <= 0 ? 60 : data.graphics.targetFps;
targetFpsSlider.mainSlider.value = v;
}
if (renderScaleSlider != null && renderScaleSlider.mainSlider != null)
{
float percent = Mathf.Clamp(data.graphics.renderScale * 100f, RenderScalePercentMin, RenderScalePercentMax);
renderScaleSlider.mainSlider.value = percent;
}
if (shadowDistanceSlider != null && shadowDistanceSlider.mainSlider != null)
{
shadowDistanceSlider.mainSlider.value = Mathf.Clamp(data.graphics.shadowDistance, shadowDistanceRange.x, shadowDistanceRange.y);
}
if (masterVolumeSlider != null && masterVolumeSlider.mainSlider != null)
{
masterVolumeSlider.mainSlider.value = Mathf.Clamp01(data.audio.master);
}
if (musicVolumeSlider != null && musicVolumeSlider.mainSlider != null)
{
musicVolumeSlider.mainSlider.value = Mathf.Clamp01(data.audio.music);
}
if (sfxVolumeSlider != null && sfxVolumeSlider.mainSlider != null)
{
sfxVolumeSlider.mainSlider.value = Mathf.Clamp01(data.audio.sfx);
}
if (mouseSensitivitySlider != null && mouseSensitivitySlider.mainSlider != null)
{
mouseSensitivitySlider.mainSlider.value = Mathf.Clamp(data.gameplay.mouseSensitivity, sensitivityRange.x, sensitivityRange.y);
}
if (llmProviderDropdown != null)
{
int p = Mathf.Clamp(data.gameplay.llmProvider, 0, 1);
EnsureDropdownInitialized(llmProviderDropdown);
if (llmProviderDropdown.items != null && llmProviderDropdown.items.Count > 0) { llmProviderDropdown.SetDropdownIndex(p); }
}
isPopulating = false;
}
private int FullscreenModeToIndex(int fullscreenMode)
{
if (fullscreenMode == (int)FullScreenMode.ExclusiveFullScreen) { return 0; }
if (fullscreenMode == (int)FullScreenMode.FullScreenWindow) { return 1; }
return 2;
}
private int IndexToFullscreenMode(int index)
{
if (index == 0) { return (int)FullScreenMode.ExclusiveFullScreen; }
if (index == 1) { return (int)FullScreenMode.FullScreenWindow; }
return (int)FullScreenMode.Windowed;
}
private int MsaaToIndex(int msaa)
{
if (msaa >= 8) { return 3; }
if (msaa >= 4) { return 2; }
if (msaa >= 2) { return 1; }
return 0;
}
private int IndexToMsaa(int index)
{
if (index == 3) { return 8; }
if (index == 2) { return 4; }
if (index == 1) { return 2; }
return 1;
}
private void MarkDirty()
{
if (isPopulating) { return; }
isDirty = true;
}
private void OnResolutionChanged(int index)
{
if (working == null) { return; }
working.graphics.resolutionIndex = -1;
if (resolutionOptions != null && index >= 0 && index < resolutionOptions.Length)
{
var opt = resolutionOptions[index];
working.graphics.resolutionWidth = opt.width;
working.graphics.resolutionHeight = opt.height;
working.graphics.resolutionRefreshRate = opt.refreshRate;
}
MarkDirty();
}
private void OnDisplayModeChanged(int index)
{
if (working == null) { return; }
working.graphics.fullscreenMode = IndexToFullscreenMode(index);
MarkDirty();
}
private void OnQualityChanged(int index)
{
if (working == null) { return; }
working.graphics.qualityLevel = index;
MarkDirty();
}
private void OnVSyncChanged(int index)
{
if (working == null) { return; }
working.graphics.vSync = index == 1;
MarkDirty();
}
private void OnMsaaChanged(int index)
{
if (working == null) { return; }
working.graphics.msaaSampleCount = IndexToMsaa(index);
MarkDirty();
}
private void OnLLMProviderChanged(int index)
{
if (working == null) { return; }
working.gameplay.llmProvider = Mathf.Clamp(index, 0, 1);
MarkDirty();
}
private void OnTextureQualityChanged(int index)
{
if (working == null) { return; }
working.graphics.textureMipmapLimit = Mathf.Clamp(index, 0, 3);
MarkDirty();
}
private void OnTargetFpsChanged(float value)
{
if (working == null) { return; }
int fps = Mathf.RoundToInt(value);
working.graphics.targetFps = fps;
MarkDirty();
}
private void OnRenderScalePercentChanged(float value)
{
if (working == null) { return; }
working.graphics.renderScale = Mathf.Clamp(value, RenderScalePercentMin, RenderScalePercentMax) / 100f;
MarkDirty();
}
private void OnShadowDistanceChanged(float value)
{
if (working == null) { return; }
working.graphics.shadowDistance = value;
MarkDirty();
}
private void OnMasterChanged(float value)
{
if (working == null) { return; }
working.audio.master = value;
MarkDirty();
}
private void OnMusicChanged(float value)
{
if (working == null) { return; }
working.audio.music = value;
MarkDirty();
}
private void OnSfxChanged(float value)
{
if (working == null) { return; }
working.audio.sfx = value;
MarkDirty();
}
private void OnSensitivityChanged(float value)
{
if (working == null) { return; }
working.gameplay.mouseSensitivity = value;
if (debugLogs) Debug.Log($"[SettingsController] Sensitivity changed -> {value}");
MarkDirty();
}
private SettingsData Clone(SettingsData data)
{
if (data == null) { return SettingsData.Default(); }
string json = JsonUtility.ToJson(data);
SettingsData copy = JsonUtility.FromJson<SettingsData>(json);
return copy ?? SettingsData.Default();
}
}
}