Centralized, designer-friendly configuration using `ScriptableObject` assets, with runtime access via `SettingsProvider` (Addressables-backed) and editor/live-preview access via `SettingsAccess`. This page follows the style of other updated docs (TOC, inline code, code-first usage, case studies).
## Table of Contents
- [What This Solves](#what-this-solves)
- [Architecture at a Glance](#architecture-at-a-glance)
- Access (runtime): `SettingsProvider` (singleton `MonoBehaviour`) — loads assets synchronously via Addressables at keys `Settings/<TypeName>` and caches them. Source: `Assets/Scripts/Core/Settings/SettingsProvider.cs`.
- Access (editor/dev): `SettingsAccess` (static) — editor-friendly shim for reading selected values even when not in Play Mode, falling back to `GameManager` at runtime. Source: `Assets/Scripts/Core/SettingsAccess.cs`.
- Editor glue (non-Play Mode): `EditorSettingsProvider` — initializes on script reload, loads assets from `Assets/Settings/*.asset`, wires delegates in `SettingsAccess` and repaints Scene views on changes. Source: `Assets/Editor/Settings/EditorSettingsProvider.cs`.
- Contracts: `SettingsInterfaces` (`IPlayerFollowerSettings`, `IInteractionSettings`, `IDivingMinigameSettings`) used by systems to remain decoupled from concrete assets. Source: `Assets/Scripts/Core/Settings/SettingsInterfaces.cs`.
- Concrete assets: `PlayerFollowerSettings`, `InteractionSettings`, `DivingMinigameSettings` — all derive from `BaseSettings` and implement their respective interfaces with validation in `OnValidate()`.
- Editor tooling: `AppleHills/Settings Editor` — finds/creates assets under `Assets/Settings` and provides a tabbed UI; `AppleHills/Developer Settings Editor` — similar pattern for developer‑only settings under `Assets/Settings/Developer`. Sources: `Assets/Editor/Settings/SettingsEditorWindow.cs`, `Assets/Editor/Settings/DeveloperSettingsEditorWindow.cs`.
At runtime, `SettingsProvider` synchronously loads settings via Addressables with keys constructed as `Settings/<TypeName>` where `<TypeName>` is the C# class name:
- Asset filenames can differ (e.g., `MinigameSettings.asset`), but Addressables keys should follow the type name to match `SettingsProvider`’s lookup.
- Mark each settings asset as Addressable and set its Addressables key as above. The provider caches objects, so subsequent `GetSettings<T>()` calls are fast.
## Design Opinion: Interface-per-Settings vs Simpler Alternatives
Your current setup creates a dedicated interface and a concrete `ScriptableObject` class per settings domain (e.g., `IInteractionSettings` + `InteractionSettings`). Here’s an assessment based on the repository:
Benefits
- Decoupling and testability: Call sites can depend on `IInteractionSettings`/`IDivingMinigameSettings`, making it trivial to mock or swap implementations in tests or temporary experiments.
- Contract discipline: Interfaces create a curated public surface for teams to converge on; designers can add fields to the asset without automatically expanding the contract.
- Runtime safety during refactors: Systems compile against the interface even if you split a single asset into multiple specialized assets later.
Costs
- Boilerplate: Duplicated getters across the interface and class; more files to maintain.
- Drift risk: If a field is added to the `ScriptableObject` but not reflected in the interface (or vice versa), consumers may not see it or may rely on the wrong surface.
- Over-abstraction for small teams: When the same team owns both the consumer and the asset, interfaces can feel heavy until real polymorphism or mocking is needed.
Pragmatic options
- Keep interfaces where they pay off now: `IInteractionSettings`, `IDivingMinigameSettings` already gate many systems and benefit from abstraction.
- Simplify where scope is narrow: For settings used in one place (or purely visual), rely on concrete classes until a second consumer appears. You can introduce an interface later without breaking Addressables keys (which use type names in lookup).
- Aggregator interface: Introduce an `IGameSettings` façade that exposes only the subset most systems need, backed by a composite provider that reads from the three assets. This reduces type spread at call sites while preserving modular assets.
- Codegen or source generators (optional): Generate interfaces from concrete classes (read-only properties only) to eliminate drift/boilerplate. Not required, just an idea if the pattern grows.
Recommendation
- Short term: Keep the three interfaces you have (interaction, follower, diving). They are already wired and provide value. Avoid adding new interfaces unless a setting has multiple independent consumers or needs mocking.
- Medium term: Consider an `IGameSettings` façade (or a small static wrapper) for high-traffic reads like interaction distances to reduce repetitive `GetSettings<T>()` calls around the codebase.