Files
AppleHillsProduction/docs/settings_readme.md
2025-10-21 15:08:48 +02:00

221 lines
12 KiB
Markdown
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.

# Apple Hills Settings System
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)
- [Quick Start (Code-First)](#quick-start-code-first)
- [Get Settings at Runtime](#get-settings-at-runtime)
- [Use Editor-Time Values via `SettingsAccess`](#use-editor-time-values-via-settingsaccess)
- [Access Example Fields](#access-example-fields)
- [Authoring in the Editor](#authoring-in-the-editor)
- [Creating/Editing Settings Assets](#creatingediting-settings-assets)
- [Addressables Keys & Loading](#addressables-keys--loading)
- [Available Settings Types](#available-settings-types)
- [Case Studies](#case-studies)
- [Tune Interaction Distances](#tune-interaction-distances)
- [Follower Handling & Movement](#follower-handling--movement)
- [Diving Minigame Tuning](#diving-minigame-tuning)
- [Troubleshooting / FAQ](#troubleshooting--faq)
- [Paths & Namespaces](#paths--namespaces)
- [Design Opinion: Interface-per-Settings vs Simpler Alternatives](#design-opinion-interface-per-settings-vs-simpler-alternatives)
- [Change Log](#change-log)
## What This Solves
- Consistent, centralized configuration across gameplay systems.
- Safe, designer-editable `ScriptableObject` assets with validation (`OnValidate`).
- Simple, Addressables-based runtime loading and caching via `SettingsProvider`.
- Editor-time overrides and scene gizmo feedback via `SettingsAccess` helpers.
## Architecture at a Glance
- Base: `BaseSettings` (`ScriptableObject`) — common parent for all settings. Implements optional `OnValidate()`.
- 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 developeronly settings under `Assets/Settings/Developer`. Sources: `Assets/Editor/Settings/SettingsEditorWindow.cs`, `Assets/Editor/Settings/DeveloperSettingsEditorWindow.cs`.
## Quick Start (Code-First)
### Get Settings at Runtime
```csharp
using AppleHills.Core.Settings;
var playerFollower = SettingsProvider.Instance.GetSettings<PlayerFollowerSettings>();
float speed = playerFollower.MoveSpeed;
```
Or fetch interaction settings once and reuse:
```csharp
using AppleHills.Core.Settings;
private IInteractionSettings _interaction;
void Awake()
{
_interaction = SettingsProvider.Instance.GetSettings<InteractionSettings>();
}
void UseIt()
{
float stopDist = _interaction.PlayerStopDistance;
}
```
### Use Editor-Time Values via `SettingsAccess`
For scene tools, gizmos, or editors that should reflect current settings outside Play Mode:
```csharp
// Returns editor-sourced values in Edit Mode; GameManager-backed in Play Mode
float stopDist = AppleHills.SettingsAccess.GetPlayerStopDistance();
float directStop = AppleHills.SettingsAccess.GetPlayerStopDistanceDirectInteraction();
float puzzleRange = AppleHills.SettingsAccess.GetPuzzlePromptRange();
```
### Access Example Fields
```csharp
using AppleHills.Core.Settings;
var pf = SettingsProvider.Instance.GetSettings<PlayerFollowerSettings>();
// Player
float moveSpeed = pf.MoveSpeed;
float accel = pf.MaxAcceleration;
bool useRb = pf.UseRigidbody;
// Follower
float followDist = pf.FollowDistance;
float near = pf.ThresholdNear;
var inter = SettingsProvider.Instance.GetSettings<InteractionSettings>();
LayerMask interactMask = inter.InteractableLayerMask;
GameObject pickupPrefab = inter.BasePickupPrefab;
float promptRange = inter.DefaultPuzzlePromptRange;
```
## Authoring in the Editor
### Creating/Editing Settings Assets
- Open via menu: `AppleHills/Settings Editor`.
- The window discovers all assets of type `BaseSettings` and provides tabbed editing for:
- `PlayerFollowerSettings`
- `InteractionSettings`
- `DivingMinigameSettings`
- If an asset is missing, the tool auto-creates it under `Assets/Settings/`:
- `Assets/Settings/PlayerFollowerSettings.asset`
- `Assets/Settings/InteractionSettings.asset`
- `Assets/Settings/DivingMinigameSettings.asset`
- Click “Save All” to persist and refresh editor providers/gizmos.
### Addressables Keys & Loading
At runtime, `SettingsProvider` synchronously loads settings via Addressables with keys constructed as `Settings/<TypeName>` where `<TypeName>` is the C# class name:
- `Settings/PlayerFollowerSettings`
- `Settings/InteractionSettings`
- `Settings/DivingMinigameSettings`
Notes:
- 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.
## Available Settings Types
- `PlayerFollowerSettings` (`IPlayerFollowerSettings`)
- Player: `MoveSpeed`, `MaxAcceleration`, `StopDistance`, `UseRigidbody`, `DefaultHoldMovementMode`.
- Follower: `FollowDistance`, `ManualMoveSmooth`, `ThresholdFar`, `ThresholdNear`, `StopThreshold`.
- Backend: `FollowUpdateInterval`, `FollowerSpeedMultiplier`, `HeldIconDisplayHeight`.
- `InteractionSettings` (`IInteractionSettings`)
- Interactions: `PlayerStopDistance`, `PlayerStopDistanceDirectInteraction`, `FollowerPickupDelay`.
- Input/Layering: `InteractableLayerMask`.
- Prefabs: `BasePickupPrefab`, `LevelSwitchMenuPrefab`, `DefaultPuzzleIndicatorPrefab`.
- Puzzle/UI: `DefaultPuzzlePromptRange`.
- Items: `CombinationRules`, `SlotItemConfigs` plus helpers `GetCombinationRule(...)`, `GetSlotItemConfig(...)`.
- `DivingMinigameSettings` (`IDivingMinigameSettings`)
- Movement, spawning, scoring, surfacing, normalized movement, tile generation, obstacles, camera viewfinder settings, photo input mode (`PhotoInputModes`).
## Case Studies
### Tune Interaction Distances
```csharp
using AppleHills.Core.Settings;
public class InteractDistanceExample
{
private readonly IInteractionSettings _s = SettingsProvider.Instance.GetSettings<InteractionSettings>();
public bool IsInRange(float dist) => dist <= _s.PlayerStopDistance;
}
```
### Follower Handling & Movement
```csharp
using AppleHills.Core.Settings;
public class FollowerMover
{
private readonly IPlayerFollowerSettings _pf = SettingsProvider.Instance.GetSettings<PlayerFollowerSettings>();
public float TargetSpeed(float error) => Mathf.Clamp(error * _pf.FollowerSpeedMultiplier, 0f, _pf.MoveSpeed);
}
```
### Diving Minigame Tuning
```csharp
using AppleHills.Core.Settings;
public class SpawnController
{
private readonly IDivingMinigameSettings _m = SettingsProvider.Instance.GetSettings<DivingMinigameSettings>();
public float NextCooldown(float baseCooldown) => Mathf.Clamp(baseCooldown + Random.Range(-_m.ObstacleSpawnIntervalVariation, _m.ObstacleSpawnIntervalVariation), 0.1f, 99);
}
```
## Troubleshooting / FAQ
- Settings return null at runtime:
- Ensure assets are Addressable with keys `Settings/<TypeName>` and Addressables are initialized before first access.
- Editor changes dont reflect in scene gizmos:
- Click “Save All” in `AppleHills/Settings Editor`; the editor provider refresh call updates views.
- Which API to use: `SettingsProvider` vs `SettingsAccess`?
- Use `SettingsProvider` in runtime code. Use `SettingsAccess` in editor tools/gizmos or shared code that runs both in Edit and Play Modes.
## Paths & Namespaces
- Scripts: `Assets/Scripts/Core/Settings/`
- `BaseSettings.cs`
- `SettingsInterfaces.cs`
- `SettingsProvider.cs`
- `PlayerFollowerSettings.cs`
- `InteractionSettings.cs`
- `DivingMinigameSettings.cs`
- Editor tooling: `Assets/Editor/Settings/`
- `SettingsEditorWindow.cs`
- `DeveloperSettingsEditorWindow.cs`
- `EditorSettingsProvider.cs`
- Editor-time facade: `Assets/Scripts/Core/SettingsAccess.cs`
- Namespaces:
- Runtime: `AppleHills.Core.Settings`
- Editor windows: `AppleHills.Core.Settings.Editor`
- Editor glue: `AppleHills.Editor`
- Facade: `AppleHills`
## 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`). Heres 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.
## Change Log
- v1.2: Expanded architecture with `EditorSettingsProvider` and developer tooling; clarified Addressables key pattern vs asset filenames; added design opinion and recommendations.
- v1.1: New page with TOC, code-first usage, authoring workflow, Addressables keys, case studies, troubleshooting, and paths.