> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monolisk.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Scene Management

> Creating and loading scenes using Roblox hierarchy or programmatic definitions

<Note>**Reading time:** \~30 minutes</Note>

Scenes in CORP organize your game world into manageable units. They can be defined using the Roblox instance hierarchy (recommended) or programmatically via TypeScript/JSON.

## Table of Contents

* [What are Scenes?](#what-are-scenes)
* [Scene from Roblox Hierarchy](#scene-from-roblox-hierarchy)
* [Scene Structure](#scene-structure)
* [Creating Scenes Programmatically](#creating-scenes-programmatically)
* [Loading Scenes](#loading-scenes)
* [Scene Serialization](#scene-serialization)
* [Best Practices](#best-practices)

## What are Scenes?

A scene is a container for GameObjects and represents a level, menu, or any other distinct state in your game. The **recommended** approach is to define scenes using Roblox Studio's instance hierarchy, though programmatic definition is also supported.

### Key Features

* **Roblox Hierarchy**: Build scenes directly in Studio using Actors and Attributes (recommended)
* **Declarative Structure**: Define your game world in code or JSON
* **Serialization**: Save and load scenes
* **Hierarchy Support**: Organize GameObjects in parent-child relationships
* **Component Configuration**: Pre-configure component properties
* **Runtime Flexibility**: Load scenes dynamically

## Scene from Roblox Hierarchy

The **primary and recommended way** to create scenes is using Roblox Studio's instance hierarchy. CORP can parse Actor instances in the workspace and convert them to GameObjects with components.

### Setting Up a Scene in Studio

1. **Create Actors**: Each Actor in Workspace becomes a GameObject
2. **Add Attributes**: Configure component properties using Roblox attributes
3. **Use Folders**: Organize your scene structure
4. **Add Components**: Use ModuleScripts or StringValues to specify components

### Example Studio Structure

```
Workspace/
├── GameScene (Folder)
│   ├── Player (Actor)
│   │   ├── $network_object (ModuleScript or Attribute)
│   │   └── player_controller (ModuleScript or Attribute)
│   ├── Enemy (Actor)
│   │   ├── $network_object
│   │   └── enemy_ai (ModuleScript)
│   │       └── attributes: attackDamage, moveSpeed
│   └── SpawnPoints (Folder)
│       ├── SpawnPoint1 (Actor)
│       └── SpawnPoint2 (Actor)
```

### Loading from Workspace

```typescript theme={null}
import { CORP } from "CORP/shared/CORP";
import { Workspace } from "@rbxts/services";

// Start CORP from workspace hierarchy
// This loads and parses all Actors in Workspace
CORP.startFromWorkspace();
```

Or start from a specific folder:

```typescript theme={null}
import { SceneManager } from "CORP/shared/scenes/scene-manager";

// Get a specific scene folder
const sceneFolder = Workspace.FindFirstChild("GameScene") as Folder;

// Load scene from that folder
if (sceneFolder) {
    SceneManager.currentScene?.startFromWorkspace(sceneFolder);
}
```

### Component Mapping in Hierarchy

Components are specified using attributes or child instances:

```typescript theme={null}
// In Studio, on an Actor:
// Add Attribute: "player_controller" = true
// Add Attribute: "speed" = 16
// Add Attribute: "health" = 100

// This creates a GameObject with a PlayerController component
// where speed and health are set to those values
```

> **Note**: Component names should use `snake_case`. Components prefixed with `$` are core CORP components (e.g., `$network_object`).

## Scene Structure

When defining scenes programmatically, they follow the `SceneDescription` interface:

```typescript theme={null}
interface SceneDescription {
    children: GameObjectDescription[];
}

interface GameObjectDescription {
    name: string;
    transform?: SerializedTransform;
    components: ComponentDescription[];
    children: GameObjectDescription[];
}

interface ComponentDescription {
    // ExportPath can be:
    // - [path, exportName]: ["src/components/MyComponent", "MyComponent"]
    // - [path]: ["src/components/MyComponent"] (uses default export)
    path: SceneSerialization.ExportPath;
    data: Record<string, any>;
}

// ExportPath type
type ExportPath = [string, string] | [string];
```

## Creating Scenes Programmatically

### Basic Scene

Create a simple scene with a few GameObjects:

```typescript theme={null}
import { SceneSerialization } from "CORP/shared/serialization/scene-serialization";

const MyScene: SceneSerialization.SceneDescription = {
    children: [
        {
            name: "Player",
            components: [
                {
                    path: ["src", "shared", "components", "PlayerController"],
                    data: {}
                },
                {
                    path: ["src", "shared", "components", "HealthSystem"],
                    data: {
                        maxHealth: 100,
                        currentHealth: 100
                    }
                }
            ],
            children: []
        },
        {
            name: "GameManager",
            components: [
                {
                    path: ["src", "shared", "components", "GameManager"],
                    data: {
                        roundDuration: 300,
                        maxPlayers: 10
                    }
                }
            ],
            children: []
        }
    ]
};

export default MyScene;
```

### Scene with Hierarchy

Create GameObjects with parent-child relationships:

```typescript theme={null}
const HierarchyScene: SceneSerialization.SceneDescription = {
    children: [
        {
            name: "Vehicle",
            components: [
                {
                    path: ["src", "shared", "components", "VehicleController"],
                    data: {
                        speed: 50,
                        turnSpeed: 2
                    }
                }
            ],
            children: [
                {
                    name: "Wheel_FrontLeft",
                    components: [
                        {
                            path: ["src", "shared", "components", "Wheel"],
                            data: { position: "front-left" }
                        }
                    ],
                    children: []
                },
                {
                    name: "Wheel_FrontRight",
                    components: [
                        {
                            path: ["src", "shared", "components", "Wheel"],
                            data: { position: "front-right" }
                        }
                    ],
                    children: []
                }
            ]
        }
    ]
};

export default HierarchyScene;
```

### Scene with Transforms

Specify initial positions for GameObjects:

```typescript theme={null}
const SpawnScene: SceneSerialization.SceneDescription = {
    children: [
        {
            name: "SpawnPoint_1",
            transform: {
                position: { x: 0, y: 5, z: 0 },
                rotation: { x: 0, y: 0, z: 0 }
            },
            components: [
                {
                    path: ["src", "shared", "components", "SpawnPoint"],
                    data: { teamId: 1 }
                }
            ],
            children: []
        },
        {
            name: "SpawnPoint_2",
            transform: {
                position: { x: 50, y: 5, z: 0 },
                rotation: { x: 0, y: 180, z: 0 }
            },
            components: [
                {
                    path: ["src", "shared", "components", "SpawnPoint"],
                    data: { teamId: 2 }
                }
            ],
            children: []
        }
    ]
};

export default SpawnScene;
```

### Scene with Network Objects

Create a scene with networked GameObjects:

```typescript theme={null}
const MultiplayerScene: SceneSerialization.SceneDescription = {
    children: [
        {
            name: "NetworkedPlayer",
            components: [
                {
                    path: ["CORP", "shared", "networking", "NetworkObject"],
                    data: {}
                },
                {
                    path: ["src", "shared", "components", "PlayerController"],
                    data: {}
                },
                {
                    path: ["src", "shared", "components", "NetworkedHealth"],
                    data: {
                        maxHealth: 100
                    }
                }
            ],
            children: []
        },
        {
            name: "GameState",
            components: [
                {
                    path: ["CORP", "shared", "networking", "NetworkObject"],
                    data: {}
                },
                {
                    path: ["src", "shared", "components", "GameState"],
                    data: {}
                }
            ],
            children: []
        }
    ]
};

export default MultiplayerScene;
```

## Loading Scenes

### Using SceneManager

The `SceneManager` class handles scene loading and management:

```typescript theme={null}
import { SceneManager } from "CORP/shared/scenes/scene-manager";
import { SceneSerialization } from "CORP/shared/serialization/scene-serialization";
import MainMenu from "./scenes/MainMenu";
import GameLevel from "./scenes/GameLevel";

// Load a scene
SceneManager.loadScene(MainMenu);

// Get the current scene
const currentScene = SceneManager.currentScene;

// Change to a different scene
SceneManager.changeScene(GameLevel);

// Unload the current scene
SceneManager.unloadScene();
```

### Starting with CORP

Use `CORP.start()` to initialize your game with a starting scene:

```typescript theme={null}
import { CORP } from "CORP/shared/CORP";
import { Configurations } from "CORP/shared/configurations";
import MainScene from "./scenes/MainScene";

// Start with a scene object
const config: Configurations.GameConfiguration = {
    startingScene: MainScene
};

CORP.start(config);
```

Or load from a path:

```typescript theme={null}
// Start with a scene path
const config: Configurations.GameConfiguration = {
    startingScene: "src/shared/scenes/MainScene"
};

CORP.start(config);
```

### Scene Events

Listen for scene changes:

```typescript theme={null}
import { SceneManager } from "CORP/shared/scenes/scene-manager";

// Subscribe to scene changes
SceneManager.sceneChanged.connect((newScene) => {
    print(`Scene changed to: ${newScene.description}`);
    
    // Perform scene-specific setup
    setupScene(newScene);
});
```

## Scene Serialization

### Exporting Scenes

Generate an Instance representation of the current scene:

```typescript theme={null}
import { SceneManager } from "CORP/shared/scenes/scene-manager";

// Generate scene instance description
const sceneInstance = SceneManager.generateSceneInstanceDescription();

// Can be used to save the scene state
```

### Importing from JSON

Scenes can be stored as JSON and loaded at runtime:

```json theme={null}
{
    "children": [
        {
            "name": "Player",
            "components": [
                {
                    "path": ["src", "shared", "components", "PlayerController"],
                    "data": {
                        "speed": 16,
                        "jumpPower": 50
                    }
                }
            ],
            "children": []
        }
    ]
}
```

Load the JSON scene:

```typescript theme={null}
import sceneData from "./scenes/level1.json";

const scene = sceneData as SceneSerialization.SceneDescription;
SceneManager.loadScene(scene);
```

### Component Path Format

Component paths use the `ExportPath` type:

```typescript theme={null}
// Format 1: [path, exportName]
{
    path: ["src/shared/components/PlayerController", "PlayerController"]
}

// Format 2: [path] (uses default export)
{
    path: ["src/shared/components/PlayerController"]
}

// For CORP built-in components
{
    path: ["CORP/shared/networking/network-object", "NetworkObject"]
}
```

> **Note**: `ExportPath` is defined as `[string, string] | [string]`, representing either `[file path, export name]` or `[file path]` for default exports.

### Configuring Components

Pass initial data to components in scenes:

```typescript theme={null}
{
    name: "Enemy",
    components: [
        {
            path: ["src", "shared", "components", "EnemyAI"],
            data: {
                // These values are applied via Object.assign
                attackDamage: 25,
                detectionRange: 50,
                moveSpeed: 8,
                patrolPoints: [
                    { x: 0, y: 0, z: 0 },
                    { x: 10, y: 0, z: 10 }
                ]
            }
        }
    ],
    children: []
}
```

The component receives these values:

```typescript theme={null}
export class EnemyAI extends Behavior {
    @SerializeField
    public attackDamage: number = 10; // Will be 25 from scene

    @SerializeField
    public detectionRange: number = 30; // Will be 50 from scene

    @SerializeField
    public moveSpeed: number = 5; // Will be 8 from scene

    @SerializeField
    public patrolPoints: Vector3[] = []; // Will have 2 points from scene

    public onStart(): void {
        // Values are already applied
        print(`Enemy with ${this.attackDamage} damage`);
    }

    protected getSourceScript(): ModuleScript {
        return script as ModuleScript;
    }

    public willRemove(): void {}
}
```

## Best Practices

### 1. Use Roblox Hierarchy (Recommended)

**Prefer defining scenes in Roblox Studio** using the instance hierarchy. This approach provides:

* Visual editing in Studio
* Easier iteration and testing
* Natural integration with Roblox's tooling
* Faster prototyping

Use programmatic scenes for:

* Procedural generation
* Dynamic content
* Runtime scene construction

### 2. Organize Scenes by Purpose

```
Workspace/
├── MainMenu (Folder)
├── Lobby (Folder)
├── Level1 (Folder)
├── Level2 (Folder)
└── GameOver (Folder)
```

Or for programmatic scenes:

```
scenes/
├── MainMenu.ts
├── Lobby.ts
├── Level1.ts
├── Level2.ts
└── GameOver.ts
```

### 2. Use Scene Hierarchy

Group related GameObjects:

```typescript theme={null}
{
    name: "Level",
    components: [],
    children: [
        {
            name: "Spawners",
            components: [],
            children: [
                { name: "SpawnPoint_1", components: [...], children: [] },
                { name: "SpawnPoint_2", components: [...], children: [] }
            ]
        },
        {
            name: "Pickups",
            components: [],
            children: [
                { name: "HealthPack_1", components: [...], children: [] },
                { name: "HealthPack_2", components: [...], children: [] }
            ]
        }
    ]
}
```

### 3. Separate Configuration

Keep configuration separate from scene structure:

```typescript theme={null}
// config.ts
export const PlayerConfig = {
    maxHealth: 100,
    speed: 16,
    jumpPower: 50
};

// scene.ts
import { PlayerConfig } from "./config";

const scene: SceneSerialization.SceneDescription = {
    children: [
        {
            name: "Player",
            components: [
                {
                    path: ["src", "shared", "components", "PlayerController"],
                    data: PlayerConfig
                }
            ],
            children: []
        }
    ]
};
```

### 4. Choose the Right Format

**For most cases, use Roblox hierarchy in Studio**. Use TypeScript/JSON for:

* Procedural content generation
* Runtime dynamic scenes
* Complex scripted setups

When using TypeScript for scenes:

* Get type safety and IntelliSense
* Enable refactoring support
* Use import statements for organization

### 5. Scene Transitions

Handle scene transitions gracefully:

```typescript theme={null}
export class SceneTransition extends Behavior {
    public async transitionToScene(newScene: SceneSerialization.SceneDescription): Promise<void> {
        // Fade out
        await this.fadeOut();

        // Clean up current scene
        if (SceneManager.currentScene) {
            SceneManager.unloadScene();
        }

        // Load new scene
        SceneManager.loadScene(newScene);

        // Fade in
        await this.fadeIn();
    }

    private async fadeOut(): Promise<void> {
        // Fade animation
    }

    private async fadeIn(): Promise<void> {
        // Fade animation
    }

    protected getSourceScript(): ModuleScript {
        return script as ModuleScript;
    }

    public onStart(): void {}
    public willRemove(): void {}
}
```

### 6. Dynamic Scene Loading

Load scenes based on game state:

```typescript theme={null}
export class LevelManager extends Behavior {
    private levels: SceneSerialization.SceneDescription[] = [];

    public onStart(): void {
        // Register levels
        this.levels = [
            require("./scenes/Level1"),
            require("./scenes/Level2"),
            require("./scenes/Level3")
        ];
    }

    public loadLevel(levelIndex: number): void {
        if (levelIndex >= 0 && levelIndex < this.levels.size()) {
            SceneManager.changeScene(this.levels[levelIndex]);
        }
    }

    protected getSourceScript(): ModuleScript {
        return script as ModuleScript;
    }

    public willRemove(): void {}
}
```

## Common Patterns

### Procedural Scene Generation

Generate scenes programmatically:

```typescript theme={null}
export function generateDungeonScene(width: number, height: number): SceneSerialization.SceneDescription {
    const children: SceneSerialization.GameObjectDescription[] = [];

    for (let x = 0; x < width; x++) {
        for (let z = 0; z < height; z++) {
            children.push({
                name: `Tile_${x}_${z}`,
                transform: {
                    position: { x: x * 10, y: 0, z: z * 10 },
                    rotation: { x: 0, y: 0, z: 0 }
                },
                components: [
                    {
                        path: ["src", "shared", "components", "FloorTile"],
                        data: {}
                    }
                ],
                children: []
            });
        }
    }

    return { children };
}

// Use it
const dungeon = generateDungeonScene(10, 10);
SceneManager.loadScene(dungeon);
```

### Scene Prefabs

Create reusable GameObject descriptions:

```typescript theme={null}
// prefabs.ts
export const PlayerPrefab: SceneSerialization.GameObjectDescription = {
    name: "Player",
    components: [
        {
            path: ["CORP", "shared", "networking", "NetworkObject"],
            data: {}
        },
        {
            path: ["src", "shared", "components", "PlayerController"],
            data: { speed: 16 }
        },
        {
            path: ["src", "shared", "components", "HealthSystem"],
            data: { maxHealth: 100 }
        }
    ],
    children: []
};

// Use in scenes
const scene: SceneSerialization.SceneDescription = {
    children: [
        PlayerPrefab,
        // ... other GameObjects
    ]
};
```

## Next Steps

* **[Decorators](./decorators)**: Learn about component decorators
* **[Advanced Topics](./advanced-topics)**: Explore advanced patterns
* **[Examples](./examples)**: See complete scene examples

***

Master scene management to organize your game world effectively! 🎬
