Move settings to Settings.json and add master volume

Replace the former per-value PlayerPrefs storage with a shared human-readable
Settings.json store (GameSettings). MouseSensitivity keeps its public .Multiplier
API and only swaps its backing to the JSON store, so the camera controllers stay
untouched. Add a MasterVolume facade that drives the AudioMixer's exposed
"MasterVolume" parameter (loaded from Resources, linear<->dB mapping), applied at
startup and on change, plus a MasterVolumeSlider binder. The default is the level
currently authored on the mixer instead of an arbitrary constant.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-03 15:30:44 +02:00
1 parent f586ef7a15
commit ba7f3c2e89
9 files changed
+290 -27

No files matched your search

+83
View File
@@ -0,0 +1,83 @@
using System;
using System.IO;
using UnityEngine;
/// Shared JSON-backed store for the player-facing game settings, written human-readable
/// to <persistentDataPath>/Settings.json. Replaces the former per-value PlayerPrefs storage.
/// Loaded lazily on first access and cached in memory (reads are plain field access, so the
/// per-frame Multiplier reads from the camera controllers never touch the disk). Each change is
/// written immediately (settings edits are rare user actions), so a hard-killed build keeps them.
///
/// The typed domain facades (MouseSensitivity, MasterVolume) own the ranges/clamping and read/write
/// through here; this class stays domain-agnostic and only persists the raw values.
public static class GameSettings
{
private const string FileName = "Settings.json";
private const int FileVersion = 1;
[Serializable]
private class Data
{
public int version = FileVersion;
public float mouseSensitivity = global::MouseSensitivity.Default;
public float masterVolume = global::MasterVolume.Default;
}
private static Data _data;
private static Data Store => _data ??= Load();
private static string SavePath() => Path.Combine(Application.persistentDataPath, FileName);
public static float MouseSensitivity
{
get => Store.mouseSensitivity;
set
{
if (Mathf.Approximately(Store.mouseSensitivity, value)) return;
Store.mouseSensitivity = value;
Save();
}
}
public static float MasterVolume
{
get => Store.masterVolume;
set
{
if (Mathf.Approximately(Store.masterVolume, value)) return;
Store.masterVolume = value;
Save();
}
}
private static Data Load()
{
string path = SavePath();
if (File.Exists(path))
{
try
{
var data = JsonUtility.FromJson<Data>(File.ReadAllText(path));
if (data != null) return data;
}
catch (Exception e)
{
Debug.LogWarning($"[GameSettings] Loading {FileName} failed, using defaults: {e.Message}");
}
}
return new Data();
}
private static void Save()
{
try
{
Directory.CreateDirectory(Application.persistentDataPath);
File.WriteAllText(SavePath(), JsonUtility.ToJson(Store, true));
}
catch (Exception e)
{
Debug.LogWarning($"[GameSettings] Saving {FileName} failed: {e.Message}");
}
}
}