# How the Scene Management System Works in Undertale-Changer-Template

> Understand the scene management system in Undertale-Changer-Template. Learn how MainControl state machine and GameUtilityService handle scene transitions with history tracking.

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

---

**The Undertale-Changer-Template scene management system relies on a centralized `MainControl` state machine that defines three logical scene states, while `GameUtilityService` provides a global API for instant and fade-out transitions with automatic navigation history tracking.**

The Undertale-Changer-Template repository implements a production-ready scene management system tailored for Undertale-like narrative games. This architecture separates scene state definitions from transition mechanics, provides automated asset generation for Overworld scenes, and ensures consistent visual and audio handling across all scene switches through a unified service layer.

## Scene State Management in MainControl

At the heart of the system lies [`MainControl.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/MainControl.cs), which maintains the current operational mode through the `SceneState` enum. This enum defines three distinct logical states—**Normal**, **Overworld**, and **Battle**—that dictate which subsystems remain active during runtime.

When the game initializes, `MainControl.Start()` invokes `StartWithSceneState()` to configure the environment based on the current state:

```csharp
private void StartWithSceneState()
{
    if (sceneState != SceneState.Overworld && OverworldPlayerBehaviour)
        Destroy(OverworldPlayerBehaviour.gameObject);

    switch (sceneState)
    {
        case SceneState.Overworld:
            // Instantiate OverworldPlayerBehaviour, bullet pools, global lighting, chase UI
            break;
        case SceneState.Battle:
            InitializationBattle();
            break;
        // Normal state requires no special initialization
    }
}

```

This method automatically cleans up Overworld-specific objects like `OverworldPlayerBehaviour` when switching away from Overworld scenes, and initializes battle subsystems when entering Battle state. The `MainControl` instance persists throughout the application lifecycle, maintaining references to the player controller, cameras, and current scene metadata at [`Assets/Scripts/UCT/Core/MainControl.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Core/MainControl.cs).

## Global Scene Switching API (GameUtilityService)

All runtime scene transitions route through [`GameUtilityService.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/GameUtilityService.cs), which exposes two primary methods for changing scenes located at [`Assets/Scripts/UCT/Service/GameUtilityService.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Service/GameUtilityService.cs).

### Instant Scene Loading

The `SwitchScene(string sceneName, bool isAsync = true)` method performs immediate scene loads with asynchronous loading enabled by default. This utility sets the canvas frame sprite, records the last visited scene (unless excluded), loads the target scene, updates resolution settings, and resets the `isSceneSwitching` flag to prevent concurrent transition attempts.

### Fade-Out Transitions

For polished visual transitions, `FadeOutAndSwitchScene` provides configurable fade effects with optional BGM muting and callback support:

```csharp
public static void FadeOutAndSwitchScene(string scene,
    Color fadeColor,
    Action action = null,
    bool isBgmMuted = false,
    float fadeTime = 0.5f,
    bool isAsync = true)
{
    action += () => SwitchScene(scene, isAsync);
    MainControl.Instance.isSceneSwitching = true;
    // BGM muting logic handles fadeTime > 0, == 0, and < 0 cases
    SettingsStorage.Pause = true;
    // UI fade image and frame color updates execute here
}

```

This method constructs an action chain that executes user-provided callbacks before invoking `SwitchScene`, pauses the game during transition, and handles three fade timing scenarios (positive, zero, and negative values) to control transition speed and direction.

### Navigation History and Excluded Scenes

`GameUtilityService` maintains an `ExcludedScenes` hash set containing `Menu`, `Rename`, `Story`, `Start`, `Battle`, and `GameOver`. The `SetLastScene()` method records the currently active scene as `playerControl.lastScene` only when the current scene does not belong to this exclusion list. This mechanism enables "return to previous" functionality throughout the UI navigation system, allowing menus to automatically return players to their prior gameplay context.

## Editor-Time Overworld Bootstrapping

The [`OverworldSceneHandler.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/OverworldSceneHandler.cs) editor script at [`Assets/Editor/Handler/OverworldSceneHandler.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Editor/Handler/OverworldSceneHandler.cs) automatically prepares new Overworld scenes when entering Play mode. Its static constructor subscribes to `EditorApplication.playModeStateChanged`, triggering validation logic upon `EnteredPlayMode`:

1. **State Validation**: Confirms the active `MainControl` instance is configured for `Overworld` state.
2. **Language Pack Generation**: Executes `CreateLanguagePackFiles` to generate empty text files in both `Resources/LanguagePacks/` and `Assets/Resources/TextAssets/LanguagePacks/` directories for the current scene name.
3. **ScriptableObject Creation**: Calls `EnsureScriptableObjects` to instantiate three required data assets under `Assets/Resources/Tables/<SceneName>/`:
   - `FactTable.asset`
   - `EventTable.asset`
   - `RuleTable.asset`

This automated pipeline ensures every new Overworld scene possesses the necessary narrative infrastructure without manual asset creation.

## Practical Scene Transition Patterns

### Story Sequence to Game Start

In [`StorySceneController.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/StorySceneController.cs) (located at [`Assets/Scripts/UCT/Scene/StorySceneController.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Scene/StorySceneController.cs)), the system detects player input during a typewriter sequence and initiates a fade transition:

```csharp
// Inside Update() method when key "Z" is pressed
GameUtilityService.FadeOutAndSwitchScene("Start", Color.black);

```

This pattern demonstrates the typical flow: detect input, stop active narrative systems, then invoke the fade service to transition to the target scene.

### Menu Navigation

[`MenuController.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/MenuController.cs) at [`Assets/Scripts/UCT/Scene/MenuController.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/Assets/Scripts/UCT/Scene/MenuController.cs) utilizes the same API for UI navigation, leveraging the excluded scenes mechanism to maintain clean navigation history when moving between configuration screens and gameplay.

## Code Examples for Common Operations

### Instant Scene Switching

Jump directly to the Battle scene without visual transition:

```csharp
GameUtilityService.SwitchScene("Battle");

```

### Configured Fade Transition

Execute a half-second fade to black while muting background music:

```csharp
GameUtilityService.FadeOutAndSwitchScene(
    scene: "Overworld",
    fadeColor: Color.black,
    isBgmMuted: true,
    fadeTime: 0.5f,
    isAsync: true,
    action: () => Debug.Log("Fade complete, loading Overworld")
);

```

### Returning to Previous Scene

Utilize the navigation history to return from a menu to the prior gameplay scene:

```csharp
var previousScene = MainControl.Instance.playerControl.lastScene;
GameUtilityService.FadeOutAndSwitchScene(previousScene, Color.black);

```

### Creating New Overworld Scenes

1. Create a scene using the `Template/Overworld` template.
2. Add the scene to Build Settings.
3. Enter Play mode to trigger automatic generation of language packs and table assets.

## Summary

- **State-Driven Architecture**: [`MainControl.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/MainControl.cs) defines three scene states (Normal, Overworld, Battle) and orchestrates subsystem initialization through `StartWithSceneState()`.
- **Unified Transition API**: [`GameUtilityService.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/GameUtilityService.cs) provides `SwitchScene()` for instant loads and `FadeOutAndSwitchScene()` for polished transitions with BGM control and callbacks.
- **Smart Navigation History**: The system tracks `lastScene` while excluding menu and utility scenes (`Menu`, `Battle`, `GameOver`, etc.) from the history stack.
- **Automated Asset Generation**: [`OverworldSceneHandler.cs`](https://github.com/arch-aik/undertale-changer-template/blob/main/OverworldSceneHandler.cs) automatically creates language pack files and ScriptableObjects (FactTable, EventTable, RuleTable) when entering Play mode on Overworld scenes.
- **Consistent Implementation**: All scene changes route through the service layer, ensuring uniform resolution updates, frame handling, and state management across the project.

## Frequently Asked Questions

### How do I trigger a scene change with a fade effect in Undertale-Changer-Template?

Call `GameUtilityService.FadeOutAndSwitchScene()` with the target scene name and desired fade color. This method accepts optional parameters for BGM muting (`isBgmMuted`), transition duration (`fadeTime`), and completion callbacks (`action`) to execute custom logic before the scene loads.

### What scenes are excluded from the navigation history?

The `GameUtilityService` maintains an `ExcludedScenes` hash set containing `Menu`, `Rename`, `Story`, `Start`, `Battle`, and `GameOver`. When switching away from these scenes, the system does not record them as the `lastScene`, ensuring that "return to previous" functionality always navigates back to meaningful gameplay contexts rather than utility screens.

### How does the system handle different scene types like Overworld and Battle?

`MainControl` uses the `SceneState` enum to distinguish between Normal, Overworld, and Battle states. During initialization, `StartWithSceneState()` destroys Overworld-specific objects (like the player controller and bullet pools) when leaving Overworld scenes, and initializes battle subsystems when entering Battle scenes, ensuring clean state transitions without object leakage.

### Do I need to manually create data assets for new Overworld scenes?

No. The `OverworldSceneHandler` editor script automatically generates required assets when you enter Play mode. It creates language pack text files in both Resources and LanguagePacks folders, and instantiates the three required ScriptableObjects—FactTable, EventTable, and RuleTable—in `Assets/Resources/Tables/<SceneName>/`.