ml::docs | Your First App Manifests Events Plugins Scenes Graphics Resources
← All Tutorials
Intermediate 4 sections ~25 min C++17/20

Scene Manager

Route between application scenes using your app's state machine. Bind scenes to state values and let SceneManager handle swapping, history, and lazy construction.

Before you start

Read Manifests and States first.

Introduction

State-Driven Scene Routing

ml::SceneManager<StateEnum> connects your app's state machine to scene objects. When you call setState(State::Game), it handles removing the outgoing scene, constructing the incoming one (if lazy), and adding it — automatically.

Design contract: setState() is the only navigation primitive. SceneManager reacts to it via onStateEnter/onStateExit callbacks — it never replaces or wraps the state machine. Your app's state machine stays the single source of truth.

bind() — Eager (always in memory)

  • Scene is a member variable of your app class
  • Constructed at startup, stays alive the whole session
  • Zero allocation cost when navigating to this scene
  • Best for: lightweight scenes visited frequently (menus, HUD)

bindLazy() — Lazy (on demand)

  • Scene heap-allocated on first visit
  • Freed when navigated away from
  • Saves memory for heavy scenes
  • Best for: large game levels, loading screens

1Core Concepts

Manifest, Bind, Attach, Start

AppManifest.h — State enum for scenes cpp
struct AppManifest : public ml::Manifest
{
    static constexpr const char* name    = "My Game";
    static constexpr const char* version = "1.0.0";

    enum class State { MainMenu, Game, Settings };
};
MyApp.h — Scene Manager wiring cpp
#pragma once
#include 
#include 
#include "AppManifest.h"
#include "MainMenuScene.h"
#include "SettingsScene.h"

class MyApp : public ml::ApplicationWith
{
    using Scenes = ml::SceneManager;
public:
    MyApp() : ApplicationWith(1280, 720, 32, "My Game") {}
    void initialization() override;
    void registerEvents()  override;

private:
    MainMenuScene _mainMenu;  // eager — always in memory
    SettingsScene _settings;  // eager
    // GameScene is lazy — not a member variable
};
MyApp.cpp — initialization() cpp
void MyApp::initialization()
{
    // Eager scenes — SceneManager does NOT own these
    Scenes::bind(State::MainMenu, _mainMenu);
    Scenes::bind(State::Settings, _settings);

    // Lazy — heap-allocated on first visit, freed on leave
    Scenes::bindLazy(State::Game);

    // With constructor arguments:
    // Scenes::bindLazy(State::Game, levelIndex, difficulty);

    // Wire into the app's state machine (registers onStateEnter/Exit)
    Scenes::attach(*this);

    // Show the first scene — does NOT push to history
    Scenes::start(State::MainMenu);
}


3Core Concepts

Writing Scene Classes

A scene is any class that inherits ml::Core — typically a ml::Component<>. It sets up its own sub-components and registers them with the manager when it becomes active.

MainMenuScene.h cpp
#pragma once
#include 
#include 
#include 

class MainMenuScene : public ml::Component<>
{
public:
    MainMenuScene()
    {
        _bg.setSize({1280.f, 720.f});
        _bg.setFillColor(sf::Color(15, 10, 30));

        _title.setString("My Game");
        _title.setCharacterSize(56);
        _title.setPosition({200.f, 160.f});

        _playBtn.setSize({200.f, 55.f});
        _playBtn.setString("Play");
        _playBtn.setPosition({540.f, 350.f});

        // Register sub-components directly — they're drawn while *this is active
        ml::CoreManager::addComponent(_bg);
        ml::CoreManager::addComponent(_title);
        ml::CoreManager::addComponent(_playBtn);
    }

    ml::RectangleButton& playButton() { return _playBtn; }

    // Required Core overrides
    void setPosition(const sf::Vector2f&) override {}
    sf::Vector2f  getPosition()     const override { return {0,0}; }
    sf::FloatRect getGlobalBounds() const override { return {{0,0},{1280,720}}; }
    void draw(sf::RenderTarget&, sf::RenderStates) const override {}

private:
    ml::Rectangle       _bg;
    ml::Text            _title;
    ml::RectangleButton _playBtn;
};