Files
Cielonos/Assets/Scripts/Settings/GameSettingsManager.cs
SoulliesOfficial 6d7ebc5825 Passion & UI
2026-06-12 17:11:39 -04:00

291 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using Cielonos.MainGame.UI;
using SLSUtilities.General;
using SLSUtilities.UI;
using SLSUtilities.WwiseAssistance;
using UnityEngine;
using UnityEngine.Localization.Settings;
namespace Cielonos.Settings
{
/// <summary>
/// 游戏设置的 Singleton 管理器。
/// <para>
/// <b>数据流:</b><br/>
/// 字段初始化器提供编译期默认值 →
/// <see cref="Awake"/> 中通过 ES3 加载磁盘数据覆盖 →
/// <see cref="Start"/> 中调用 <see cref="ApplyAll"/> 推送到各子系统。
/// </para>
/// <para>
/// <b>扩展方式:</b>直接在对应 Settings 类(<see cref="GameplaySettings"/>
/// / <see cref="GraphicsSettings"/> / <see cref="SoundSettings"/>
/// / <see cref="ControlsSettings"/>)中添加带默认值的新字段。
/// ES3 反序列化时,旧存档缺失的字段自动获取字段初始化器的默认值。
/// 然后在对应的 Apply 方法中添加新字段的应用逻辑即可。
/// </para>
/// </summary>
public class GameSettingsManager : Singleton<GameSettingsManager>
{
// ──────────────────────── 持久化常量 ────────────────────────
private const string SaveFileName = "GameSettings.es3";
private const string KeyGameplay = "gameplay";
private const string KeyGraphics = "graphics";
private const string KeySound = "sound";
private const string KeyControls = "controls";
// ──────────────────────── Wwise RTPC 名称 ──────────────────
private const string RtpcMasterVolume = "MasterVolume";
private const string RtpcMusicVolume = "MusicVolume";
private const string RtpcSfxVolume = "SFXVolume";
// ──────────────────────── 运行时数据 ───────────────────────
public GameplaySettings gameplay = new();
public GraphicsSettings graphics = new();
public SoundSettings sound = new();
public ControlsSettings controls = new();
// ──────────────────────── 事件 ─────────────────────────────
/// <summary>Gameplay 设置被应用后触发。</summary>
public static event Action OnGameplaySettingsApplied;
/// <summary>Graphics 设置被应用后触发。</summary>
public static event Action OnGraphicsSettingsApplied;
/// <summary>Sound 设置被应用后触发。</summary>
public static event Action OnSoundSettingsApplied;
/// <summary>Controls 设置被应用后触发。</summary>
public static event Action OnControlsSettingsApplied;
// ──────────────────────── 生命周期 ─────────────────────────
protected override void Awake()
{
base.Awake();
Load();
}
private void Start()
{
ApplyAll();
}
// ──────────────────────── 持久化 ───────────────────────────
/// <summary>
/// 将所有设置分类保存到磁盘。每个分类使用独立的 ES3 Key
/// 某个分类数据损坏时不影响其他分类。
/// </summary>
public void Save()
{
try
{
ES3.Save(KeyGameplay, gameplay, SaveFileName);
ES3.Save(KeyGraphics, graphics, SaveFileName);
ES3.Save(KeySound, sound, SaveFileName);
ES3.Save(KeyControls, controls, SaveFileName);
Debug.Log("[GameSettingsManager] Settings saved.");
}
catch (Exception e)
{
Debug.LogError($"[GameSettingsManager] Save failed: {e.Message}");
}
}
/// <summary>
/// 从磁盘加载设置。每个分类独立加载,某个分类缺失或损坏时
/// 保留字段初始化器的默认值。
/// </summary>
public void Load()
{
try
{
if (!ES3.FileExists(SaveFileName)) return;
if (ES3.KeyExists(KeyGameplay, SaveFileName))
gameplay = ES3.Load<GameplaySettings>(KeyGameplay, SaveFileName);
if (ES3.KeyExists(KeyGraphics, SaveFileName))
graphics = ES3.Load<GraphicsSettings>(KeyGraphics, SaveFileName);
if (ES3.KeyExists(KeySound, SaveFileName))
sound = ES3.Load<SoundSettings>(KeySound, SaveFileName);
if (ES3.KeyExists(KeyControls, SaveFileName))
controls = ES3.Load<ControlsSettings>(KeyControls, SaveFileName);
}
catch (Exception e)
{
Debug.LogError($"[GameSettingsManager] Load failed: {e.Message}");
}
}
/// <summary>
/// 删除设置存档文件。
/// </summary>
public void DeleteSaveFile()
{
try
{
if (ES3.FileExists(SaveFileName))
{
ES3.DeleteFile(SaveFileName);
Debug.Log("[GameSettingsManager] Save file deleted.");
}
}
catch (Exception e)
{
Debug.LogError($"[GameSettingsManager] Delete save failed: {e.Message}");
}
}
// ──────────────────────── Apply ────────────────────────────
/// <summary>
/// 应用所有分类的设置到对应系统。
/// </summary>
public void ApplyAll()
{
ApplyGameplay();
ApplyGraphics();
ApplySound();
ApplyControls();
}
/// <summary>
/// 应用 Gameplay 设置。
/// </summary>
public void ApplyGameplay()
{
// Locale
var availableLocales = LocalizationSettings.AvailableLocales;
if (availableLocales != null)
{
var locale = availableLocales.GetLocale(gameplay.locale);
if (locale != null)
LocalizationSettings.SelectedLocale = locale;
}
// FPS 显示
if (PlayerCanvas.Instance != null)
PlayerCanvas.Instance.frameRateText.gameObject.SetActive(gameplay.showFPS);
OnGameplaySettingsApplied?.Invoke();
}
/// <summary>
/// 应用 Graphics 设置。
/// </summary>
public void ApplyGraphics()
{
// 分辨率与全屏模式
if (graphics.resolutionWidth > 0 && graphics.resolutionHeight > 0)
{
Screen.SetResolution(
graphics.resolutionWidth,
graphics.resolutionHeight,
graphics.fullscreenMode
);
}
else
{
Screen.fullScreenMode = graphics.fullscreenMode;
}
// 画质预设
if (graphics.qualityLevel >= 0)
QualitySettings.SetQualityLevel(graphics.qualityLevel, applyExpensiveChanges: true);
// VSync
QualitySettings.vSyncCount = graphics.vSync ? 1 : 0;
// 帧率上限
Application.targetFrameRate = graphics.targetFrameRate;
OnGraphicsSettingsApplied?.Invoke();
}
/// <summary>
/// 应用 Sound 设置(通过 Wwise RTPC
/// </summary>
public void ApplySound()
{
if (AudioManager.Instance == null) return;
AudioManager.Instance.SetRTPC(RtpcMasterVolume, sound.masterVolume);
AudioManager.Instance.SetRTPC(RtpcMusicVolume, sound.musicVolume);
AudioManager.Instance.SetRTPC(RtpcSfxVolume, sound.sfxVolume);
OnSoundSettingsApplied?.Invoke();
}
/// <summary>
/// 应用 Controls 设置。
/// <para>
/// 按键绑定覆盖由 <c>PlayerInputSubcontroller</c> 消费:
/// 在其 <c>Initialize()</c> 中读取 <see cref="controls"/>.<see cref="ControlsSettings.bindingOverridesJson"/>
/// 并调用 <c>InputActionAsset.LoadBindingOverridesFromJson()</c>
/// 然后重新初始化 <see cref="InputBindingResolver"/>。
/// </para>
/// </summary>
public void ApplyControls()
{
OnControlsSettingsApplied?.Invoke();
}
// ──────────────────────── Reset ────────────────────────────
/// <summary>
/// 重置所有设置为默认值并立即应用。
/// </summary>
public void ResetAllToDefault()
{
gameplay = new GameplaySettings();
graphics = new GraphicsSettings();
sound = new SoundSettings();
controls = new ControlsSettings();
ApplyAll();
}
/// <summary>
/// 重置 Gameplay 设置为默认值并立即应用。
/// </summary>
public void ResetGameplayToDefault()
{
gameplay = new GameplaySettings();
ApplyGameplay();
}
/// <summary>
/// 重置 Graphics 设置为默认值并立即应用。
/// </summary>
public void ResetGraphicsToDefault()
{
graphics = new GraphicsSettings();
ApplyGraphics();
}
/// <summary>
/// 重置 Sound 设置为默认值并立即应用。
/// </summary>
public void ResetSoundToDefault()
{
sound = new SoundSettings();
ApplySound();
}
/// <summary>
/// 重置 Controls 设置为默认值并立即应用。
/// </summary>
public void ResetControlsToDefault()
{
controls = new ControlsSettings();
ApplyControls();
}
}
}