Intermediate
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.
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;
};