using TMPro; using UnityEngine; using UnityEngine.UI; using BaseGames.Core; using BaseGames.Localization; namespace BaseGames.UI { /// /// 设置面板控制器(架构 10_UIModule §7)。 /// 驱动 SettingsManager 的音量与画面设置,并从当前配置初始化控件值。 /// 生命周期 / 焦点由 统一处理。 /// public class SettingsPanelController : UIPanelBase { // ISettingsService 通过 ServiceLocator 获取,无需 Inspector 直接注入具体类, // 支持测试场景替换 Mock 实现。 private ISettingsService _settings; [Header("音量滑条")] [SerializeField] private Slider _masterVolume; [SerializeField] private Slider _bgmVolume; [SerializeField] private Slider _sfxVolume; [SerializeField] private Slider _ambientVolume; [Header("画面")] [SerializeField] private Toggle _vSyncToggle; [SerializeField] private TMP_Dropdown _fpsDropdown; // 30 / 60 / 120 / 无限 [Header("可访问性")] [SerializeField] private Slider _uiScaleSlider; // 0.8 ~ 1.5 [SerializeField] private TMP_Text _uiScaleValueText; // 实时显示 "100%" [SerializeField] private TMP_Dropdown _colorblindDropdown; // None / Prot / Deut / Trit [SerializeField] private Toggle _screenShakeToggle; [Header("语言")] [SerializeField] private TMP_Dropdown _languageDropdown; // 中文 / English / 日本語 / 한국어 [Header("按键重绑定")] [SerializeField] private GameObject _rebindPanelRoot; // RebindPanel GameObject private static readonly int[] FpsOptions = { 30, 60, 120, -1 }; // 语言下拉项顺序(与脚手架填充的显示项一一对应) private static readonly Language[] LanguageOptions = { Language.ChineseSimplified, Language.English, Language.Japanese, Language.Korean }; protected override void OnPanelOpen() { _settings = ServiceLocator.GetOrDefault(); if (_settings == null) return; var data = _settings.Current; // 初始化控件值(先移除监听再设置值再添加,防止面板重开时重复注册) InitSlider(_masterVolume, data.MasterVolume, v => _settings.SetMasterVolume(v)); InitSlider(_bgmVolume, data.BGMVolume, v => _settings.SetBGMVolume(v)); InitSlider(_sfxVolume, data.SFXVolume, v => _settings.SetSFXVolume(v)); InitSlider(_ambientVolume,data.AmbientVolume, v => _settings.SetAmbientVolume(v)); if (_vSyncToggle != null) { _vSyncToggle.onValueChanged.RemoveAllListeners(); _vSyncToggle.isOn = data.VSync; _vSyncToggle.onValueChanged.AddListener(v => _settings.SetVSync(v)); } if (_fpsDropdown != null) { _fpsDropdown.onValueChanged.RemoveAllListeners(); int idx = System.Array.IndexOf(FpsOptions, data.TargetFPS); _fpsDropdown.value = idx >= 0 ? idx : 1; // default 60 _fpsDropdown.onValueChanged.AddListener(i => _settings.SetTargetFrameRate(FpsOptions[Mathf.Clamp(i, 0, FpsOptions.Length - 1)])); } // ── 可访问性 ────────────────────────────────────────────────────── if (_uiScaleSlider != null) { _uiScaleSlider.onValueChanged.RemoveAllListeners(); _uiScaleSlider.minValue = 0.8f; _uiScaleSlider.maxValue = 1.5f; _uiScaleSlider.value = Mathf.Clamp(data.UIScale, _uiScaleSlider.minValue, _uiScaleSlider.maxValue); UpdateUIScaleLabel(_uiScaleSlider.value); _uiScaleSlider.onValueChanged.AddListener(v => { _settings.SetUIScale(v); UpdateUIScaleLabel(v); }); } if (_colorblindDropdown != null) { _colorblindDropdown.onValueChanged.RemoveAllListeners(); _colorblindDropdown.value = (int)data.ColorblindMode; _colorblindDropdown.onValueChanged.AddListener(i => _settings.SetColorblindMode((ColorblindMode)Mathf.Clamp(i, 0, 3))); } if (_screenShakeToggle != null) { _screenShakeToggle.onValueChanged.RemoveAllListeners(); _screenShakeToggle.isOn = data.ScreenShakeEnabled; _screenShakeToggle.onValueChanged.AddListener(v => _settings.SetScreenShakeEnabled(v)); } // ── 语言 ────────────────────────────────────────────────────────── if (_languageDropdown != null) { _languageDropdown.onValueChanged.RemoveAllListeners(); var loc = ServiceLocator.GetOrDefault(); int idx = loc != null ? System.Array.IndexOf(LanguageOptions, loc.CurrentLanguage) : 0; _languageDropdown.value = idx >= 0 ? idx : 0; _languageDropdown.RefreshShownValue(); _languageDropdown.onValueChanged.AddListener(i => ServiceLocator.GetOrDefault()? .SetLanguage(LanguageOptions[Mathf.Clamp(i, 0, LanguageOptions.Length - 1)])); } } private void UpdateUIScaleLabel(float v) { if (_uiScaleValueText != null) _uiScaleValueText.text = Mathf.RoundToInt(v * 100f) + "%"; } // ── 辅助 ────────────────────────────────────────────────────────────── private static void InitSlider(Slider slider, float value, UnityEngine.Events.UnityAction onChange) { if (slider == null) return; slider.onValueChanged.RemoveAllListeners(); slider.value = value; slider.onValueChanged.AddListener(onChange); } // ── 焦点 ────────────────────────────────────────────────────────────── /// 默认焦点 / 焦点恢复回到主音量滑条(基类 FocusFirst 调用)。 protected override GameObject ResolveFirstSelected() => _masterVolume != null ? _masterVolume.gameObject : null; } }