Engine architecture
Follow the main loop, EngineContext, object ownership, and the relationships between systems.
On this page
From input to frame
main() constructs an Engine and repeats three operations while the window remains open. This excerpt comes from the reference code:
while (engine.IsRunning()) {
engine.ProcessEvent();
engine.Update();
if (!engine.HasFocus()) {
sf::sleep(sf::milliseconds(10));
continue;
}
engine.Render();
}
Without focus, the loop continues processing events and updating the clock; it skips rendering and sleeps for 10 ms. Engine::Update also checks focus before advancing the scene.
Engine responsibilities
Engine owns the window, an EngineContext, the scene registry, the reference to the active scene, and a pause overlay. Its role is to coordinate these elements.
Engine
├── sf::RenderWindow
├── EngineContext
│ ├── renderer · resources · particles
│ ├── input · cursor · gui
│ ├── audio · time · random
│ └── scenes · save · screenshot · clipboard
├── SceneFactory::Scenes
│ └── name → unique_ptr<Scene>
├── currentScene → active Scene
└── Overlay → TGUI widgets
The context is passed by reference to each scene. A game can therefore access ctx.renderer, ctx.input, or ctx.resources without constructing duplicate services.
Event processing
At the start of ProcessEvent, the engine consumes any pending scene change. It then retrieves events from sf::RenderWindow and distributes them:
EngineVisitorhandles closing, focus, resizing, controllers, and global shortcuts.GuiManagersends the event to TGUI.- The scene receives
OnEventwhen the overlay is hidden. - The engine processes any overlay selection and synchronizes the pause state.
The GUI and scene participate in the same processing flow; there is no separate documented event bus.
Updates and time
TimeManager calculates the time elapsed since the previous frame and caps it at gConfig.maximumDeltaTime. The cursor is then updated, and Scene::Update is called if the overlay is hidden.
The system does not implement a fixed-step accumulator. Games decide how to use delta time and their own timers.
In the reference revision, each scene implements its own response to OnPause, including audio and timers. A local cooldown clock refactor exists, marked as in development, and is not part of this public reference API.
Rendering and layers
The scene draws onto an sf::RenderTexture. RenderManager applies the effect chain and returns the final texture. Engine presents it in the window, then draws the GUI and cursor on top.
Scene::Render
↓
RenderManager / RenderTexture
↓
Bloom → Monitor
↓
Window → GUI → Cursor → display
Effects are only added when sf::Shader::isAvailable() returns true. The GUI and cursor are drawn after post-processing. See Rendering to follow the Bloom passes.
Ownership and memory
- Scenes are stored in an
unordered_mapofunique_ptr<Scene>. - Effects are stored in a vector of
unique_ptr<Effect>. - The active scene is a non-owning pointer; its object still belongs to the registry.
- Managers are members of
EngineContextby value. - ResourceManager keeps resources by value inside maps.
The project uses RAII and standard containers. There is no custom memory manager or generic ECS. All registered scenes are constructed in CreateScenes; changing scenes does not construct a new instance on every transition.
Code organization
| Directory | Contents |
|---|---|
Engine/Include |
Core contracts and types, managers, scenes, effects, and utilities. |
Engine/Source |
Implementations and entry point. |
Games/Include |
Game classes, types, and configuration constants. |
Games/Source |
Game logic and rendering. |
Content |
Graphics and audio assets, fonts, shaders, theme, and JSON. |