# Key Components of the Settings System in Undertale-Changer Template: A Data-Driven Architecture Guide

> Explore the data-driven settings system in the Undertale Changer Template. Understand how separate layers for persistence UI and navigation simplify menu option declaration without custom code.

- Repository: [Archived AIk/undertale-changer-template](https://github.com/arch-aik/undertale-changer-template)
- Tags: architecture
- Published: 2026-02-25

---

**The settings system uses a data-driven architecture that separates persisted values, UI definitions, and navigation layers, allowing developers to declare menu options without writing custom rendering logic.**

The Undertale-Changer template provides a modular, extensible settings subsystem built for Unity. Understanding the key components of the settings system enables developers to customize video, audio, and input configurations while maintaining clean separation between data storage and presentation logic.

## The Three-Pillar Architecture

The system organizes functionality into three distinct concerns:

1. **Data Persistence** – Static storage for runtime values
2. **Option Definition** – Declarative descriptions of menu items
3. **Layer Management** – Hierarchical grouping of related options

This separation allows the `SettingsController` to render any menu generically, driving the UI purely from the metadata defined in each `SettingsOption`.

## SettingsStorage: The Persistent Value Layer

`SettingsStorage` is a static class located at [`Assets/Scripts/UCT/Settings/SettingsStorage.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/SettingsStorage.cs) that acts as the single source of truth for all configurable values. It exposes properties for resolution, volume levels, key bindings, and fullscreen state that both the UI and game systems read directly.

The class maintains a dictionary called `CubismSettingsLayers` that stores the hierarchy of available menu layers, enabling runtime navigation between settings categories.

## SettingsOption: Defining Menu Items

`SettingsOption` (defined in [`Assets/Scripts/UCT/Settings/SettingsOption.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/SettingsOption.cs)) represents a single entry in the settings menu. Each instance declares:

- **Display metadata** – `DataName` and `DescriptionDataName` for localization
- **Option type** – `SelectionToggle`, `SelectionBasedFalse`, `ConfigurableKeyTrue`, or `SelectionBasedTrue`
- **Data binding** – Getter and setter delegates (`SelectionBasedChangedValueGetter`/`Setter`) that link the UI to `SettingsStorage`
- **Interaction callbacks** – `OnSelected` actions triggered when the user activates the option

This declarative approach allows complex options like volume sliders or key rebinders to be defined with simple property assignments rather than custom UI code.

## ISettingsLayer and Concrete Implementations

The `ISettingsLayer` interface and `SettingsLayerBase` abstract class (located in [`Assets/Scripts/UCT/Settings/ISettingsLayer.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/ISettingsLayer.cs)) provide the structure for grouping related options into menu screens.

Each concrete layer inherits from `SettingsLayerBase` and populates its `AllSettingsOptions` list in the constructor. The template includes eight predefined layers defined as region blocks within [`ISettingsLayer.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/ISettingsLayer.cs):

- **HomeSettingsLayer** – Root menu entry point
- **VideoSettingsLayer** – Resolution, fullscreen, VSync, and frame-rate caps
- **AudioSettingsLayer** – Master, BGM, and SFX volume controls
- **GraphicSettingsLayer** – Quality presets and effect simplification
- **InputSettingsLayer** – Navigation entry for key binding configuration
- **SettingKeyControlLayer** – Concrete key rebinders for actions like MoveUp, Confirm, and Settings
- **SettingLanguagePackageLayer** – Localization pack selection
- **SubtitleSettingsLayer** – Text display speed and subtitle toggles

The base class provides helper methods like `AddBackOptionForDisplay()` and `AddConfigurableKeyOption()` to standardize common UI patterns across layers.

## SettingsController: The UI Driver

`SettingsController` (in [`Assets/Scripts/UCT/Settings/SettingsController.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/SettingsController.cs)) orchestrates the runtime behavior of the settings menu. It maintains the navigation stack (`_settingsPreviousLayers`), tracks the currently selected option index, and rebuilds the displayed text whenever the user changes layers or modifies values.

The controller queries the active layer's `AllSettingsOptions` collection to determine available choices, invokes the getter delegates to display current values, and executes setter delegates when the user confirms changes. This generic handling allows the controller to manage any future layer without modification.

## Practical Implementation Examples

### Reading a Setting from Gameplay Code

Access persisted values directly through the static `SettingsStorage` class:

```csharp
// Check fullscreen state before adjusting UI layout
if (SettingsStorage.FullScreen)
{
    canvasScaler.referenceResolution = new Vector2(1920, 1080);
}

```

### Creating a Custom Settings Layer

Define a new menu screen by inheriting from `SettingsLayerBase`:

```csharp
public class AccessibilitySettingsLayer : SettingsLayerBase
{
    public AccessibilitySettingsLayer()
    {
        // Toggle for high contrast mode
        AllSettingsOptions.Add(new SettingsOption(false)
        {
            DataName = "HighContrast",
            DescriptionDataName = new[] { "HighContrastTip" },
            Type = OptionType.SelectionToggle,
            SelectionBasedChangedValueGetter = () => SettingsStorage.HighContrast,
            SelectionBasedChangedValueSetter = v => SettingsStorage.HighContrast = (bool)v,
            OnSelected = () => SettingsStorage.HighContrast = !SettingsStorage.HighContrast
        });

        AddBackOptionForDisplay(null);
    }
}

```

### Registering and Navigating to Custom Layers

Add the layer to the storage dictionary during initialization:

```csharp
// In your bootstrap or GameManager
SettingsStorage.CubismSettingsLayers["Accessibility"] = new AccessibilitySettingsLayer();

// Navigate from another layer
_settingsPreviousLayers.Add(currentLayer);
settingsLayer = "Accessibility";
_settingSelectedOption = 0;

```

### Implementing a Numeric Slider Option

Use `SelectionBasedFalse` with percentage display for volume-like controls:

```csharp
AllSettingsOptions.Add(new SettingsOption(0.5f)
{
    DataName = "DialogueVolume",
    DescriptionDataName = new[] { "DialogueVolumeTip" },
    Type = OptionType.SelectionBasedFalse,
    OptionDisplayMode = OptionType.Percentage,
    SelectionBasedChangedValueGetter = () => SettingsStorage.DialogueVolume,
    SelectionBasedChangedValueSetter = v => SettingsStorage.DialogueVolume = (float)v,
    SelectionBasedChangedUnit = 0.05f,
    SelectionBasedChangedMin = 0f,
    SelectionBasedChangedMax = 1f
});

```

## Summary

- **SettingsStorage** ([`Assets/Scripts/UCT/Settings/SettingsStorage.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/SettingsStorage.cs)) acts as the static source of truth for all configurable values and maintains the layer hierarchy in `CubismSettingsLayers`.

- **SettingsOption** ([`Assets/Scripts/UCT/Settings/SettingsOption.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/SettingsOption.cs)) provides a declarative model for menu items, binding display text to getter/setter delegates that read from and write to `SettingsStorage`.

- **ISettingsLayer** ([`Assets/Scripts/UCT/Settings/ISettingsLayer.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/ISettingsLayer.cs)) defines the interface and abstract base for grouping options into screens, with concrete implementations like `VideoSettingsLayer` and `AudioSettingsLayer` populating `AllSettingsOptions` in their constructors.

- **SettingsController** ([`Assets/Scripts/UCT/Settings/SettingsController.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Settings/SettingsController.cs)) drives the runtime UI, managing navigation stacks, rendering text based on the active layer's options, and invoking delegates when users modify values.

## Frequently Asked Questions

### How do I add a new settings category to the menu?

Create a class inheriting from `SettingsLayerBase`, populate `AllSettingsOptions` in the constructor with your desired `SettingsOption` instances, then register the instance in `SettingsStorage.CubismSettingsLayers` with a unique string key. The `SettingsController` will automatically handle navigation and rendering once registered.

### What is the difference between SelectionToggle and SelectionBasedFalse option types?

`SelectionToggle` represents binary on/off states where the value inverts when the user selects the option, typically used for boolean settings like fullscreen mode. `SelectionBasedFalse` represents ranged numeric values where left/right inputs decrement or increment the value within defined min/max bounds, commonly used for volume sliders or sensitivity settings.

### How does the settings system persist data between game sessions?

While the analysis focuses on the runtime architecture, `SettingsStorage` serves as the central value holder that typically serializes to Unity's `PlayerPrefs` or a custom JSON file on application quit. The static properties in `SettingsStorage` ensure that any changes made through `SettingsOption` delegates are immediately available to the rest of the game without requiring additional load calls.

### Can I customize the navigation behavior or visual presentation of individual options?

Yes, through the `SettingsOption` metadata properties. You can specify `DescriptionDataName` arrays for tooltip text, set `OptionDisplayMode` to formats like `Percentage` for numeric values, and provide custom `OnSelected` callbacks for immediate side effects. However, the actual rendering logic resides in `SettingsController`, so significant visual changes would require modifying how the controller interprets the option metadata.