2025-10-10 15:47:38 +02:00
# Apple Hills Settings System
2025-10-21 12:37:53 +02:00
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 )
- [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.
- Access (editor/dev): `SettingsAccess` (static) — editor-friendly shim for reading selected values even when not in Play Mode, falling back to `GameManager` at runtime.
- Contracts: `SettingsInterfaces` (`IPlayerFollowerSettings` , `IInteractionSettings` , `IDivingMinigameSettings` ) used by systems to remain decoupled from concrete assets.
- Editor tooling: `AppleHills/Settings Editor` — finds/creates assets under `Assets/Settings` and provides a tabbed UI.
## Quick Start (Code-First)
### Get Settings at Runtime
2025-10-10 15:47:38 +02:00
```csharp
2025-10-21 12:37:53 +02:00
using AppleHills.Core.Settings;
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
var playerFollower = SettingsProvider.Instance.GetSettings< PlayerFollowerSettings > ();
float speed = playerFollower.MoveSpeed;
2025-10-10 15:47:38 +02:00
```
2025-10-21 12:37:53 +02:00
Or fetch interaction settings once and reuse:
2025-10-10 15:47:38 +02:00
```csharp
2025-10-21 12:37:53 +02:00
using AppleHills.Core.Settings;
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
private IInteractionSettings _interaction;
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
void Awake()
2025-10-10 15:47:38 +02:00
{
2025-10-21 12:37:53 +02:00
_interaction = SettingsProvider.Instance.GetSettings< InteractionSettings > ();
2025-10-10 15:47:38 +02:00
}
2025-10-21 12:37:53 +02:00
void UseIt()
2025-10-10 15:47:38 +02:00
{
2025-10-21 12:37:53 +02:00
float stopDist = _interaction.PlayerStopDistance;
2025-10-10 15:47:38 +02:00
}
```
2025-10-21 12:37:53 +02:00
### 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();
```
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
### Access Example Fields
2025-10-10 15:47:38 +02:00
```csharp
using AppleHills.Core.Settings;
2025-10-21 12:37:53 +02:00
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;
2025-10-10 15:47:38 +02:00
```
2025-10-21 12:37:53 +02:00
## 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:
- `Settings/PlayerFollowerSettings`
- `Settings/InteractionSettings`
- `Settings/DivingMinigameSettings`
Ensure these assets are marked as Addressables with the exact keys 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
2025-10-10 15:47:38 +02:00
```csharp
2025-10-21 12:37:53 +02:00
using AppleHills.Core.Settings;
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
public class InteractDistanceExample
2025-10-10 15:47:38 +02:00
{
2025-10-21 12:37:53 +02:00
private readonly IInteractionSettings _s = SettingsProvider.Instance.GetSettings< InteractionSettings > ();
public bool IsInRange(float dist) => dist < = _s.PlayerStopDistance;
2025-10-10 15:47:38 +02:00
}
```
2025-10-21 12:37:53 +02:00
### Follower Handling & Movement
2025-10-10 15:47:38 +02:00
```csharp
2025-10-21 12:37:53 +02:00
using AppleHills.Core.Settings;
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
public class FollowerMover
2025-10-10 15:47:38 +02:00
{
2025-10-21 12:37:53 +02:00
private readonly IPlayerFollowerSettings _pf = SettingsProvider.Instance.GetSettings< PlayerFollowerSettings > ();
public float TargetSpeed(float error) => Mathf.Clamp(error * _pf.FollowerSpeedMultiplier, 0f, _pf.MoveSpeed);
2025-10-10 15:47:38 +02:00
}
```
2025-10-21 12:37:53 +02:00
### Diving Minigame Tuning
2025-10-10 15:47:38 +02:00
```csharp
2025-10-21 12:37:53 +02:00
using AppleHills.Core.Settings;
2025-10-10 15:47:38 +02:00
2025-10-21 12:37:53 +02:00
public class SpawnController
2025-10-10 15:47:38 +02:00
{
2025-10-21 12:37:53 +02:00
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);
2025-10-10 15:47:38 +02:00
}
```
2025-10-21 12:37:53 +02:00
## Troubleshooting / FAQ
- Settings return null at runtime:
- Ensure assets are Addressable with keys `Settings/<TypeName>` and Addressables are initialized before first access.
- Editor changes don’ t 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/SettingsEditorWindow.cs`
- Editor-time facade: `Assets/Scripts/Core/SettingsAccess.cs`
- Namespaces:
- Runtime: `AppleHills.Core.Settings`
- Editor window: `AppleHills.Core.Settings.Editor`
- Facade: `AppleHills`
## Change Log
- v1.1: New page with TOC, code-first usage, authoring workflow, Addressables keys, case studies, troubleshooting, and paths.