Create a UIController
Give the game’s interface one owner for its TGUI session, input policy, and
retained scene. HUD, menu, and chat components can share that session through
borrowed references. The application defines this UIController; game rules
continue to own values such as health and score.
Start with First Game, or continue from Create a Character. This lesson establishes the shared lifecycle. Create a HUD adds the first visible component.
The enclosing UI owner manages the session’s lifetime. Components borrow that session rather than creating a separate session for each panel:
flowchart TB
accTitle: A UI owner with one shared session
accDescr: The game owns its UIController. That controller owns a TGUI session and the components added in later lessons; each component borrows the session while managing its widgets.
game["Game"] -->|owns| controller["UIController"]
controller -->|owns| session["Shared TGUI session"]
controller -->|owns added components| components["UI components"]
components -->|borrow| session
Select TGUI
Enable CHERYL_BUILD_UI_TGUI=ON in First Game’s source configuration, or use an
SDK containing that module. In Project setup’s copied CMake project, change its
local set(CHERYL_BUILD_UI_TGUI OFF) to ON before add_subdirectory(); those
scoped module selections take precedence over a
same-name command-line cache option. Add the UI link to your executable target:
target_link_libraries(game PRIVATE Cheryl::UI::TGUI)
Use your target’s name in place of game. Prepare the pinned toolkit through
Project setup. Enabling the module does not download it.
The uploader obtains compatible default materials from the provider. OpenGL embeds
their shaders, so this example needs no additional shader or material files.
Assign the execution owners
The session runs widget logic, callbacks, layout, and CPU scene recording on the simulation owner. The platform owner polls native input, creates/uploads graphics resources, and renders the retained scene. Keeping callbacks with simulation lets them update UI intent alongside the game state they observe.
Game hook |
Owner |
UI work |
|---|---|---|
Platform, before simulation starts. |
Configure fonts and construct the controller’s uploader from the live provider. |
|
Simulation, before Ready and the first update. |
Construct the session with automatic view following, receive its initial size, then create widgets. |
|
Each |
Simulation. |
Runtime applies view changes before the hook; deliver input, advance toolkit time, update components, and publish available scene replacements. |
Simulation. |
Append the latest complete UI scene after the world pass. |
|
Platform, after simulation joins and accepted work settles. |
Release component-held toolkit references, then close the session. |
In sequential mode platform and simulation use the same thread. Concurrent mode
uses a dedicated simulation thread. init_simulation() supplies the creating
owner and the initial view in both modes. All live session/widget operations use
that owner. Final session closure stays in deinit_platform() after UI
quiescence and accepted work have settled.
Connect the controller to the game
Include ui-controller.h in the game header and add a member:
std::optional<Tutorial::UIController> ui_;
During init_platform(), after graphics initialization and any font configuration:
ui_.emplace(engine_);
Declare void init_simulation() override; in the game class, then define it
alongside the other hooks:
void Game::init_simulation() {
ui_->init_simulation();
}
After quit handling and gameplay changes in update():
ui_->update(tick);
After world draws in prepare_render_frame():
ui_->append(frame);
During deinit_platform(), after the runtime has joined simulation and settled accepted work:
if (ui_)
ui_->close_after_quiescence();
ui_.reset();
The controller retains the latest complete uploaded scene while at most one
replacement is pending. adopt_scene() checks readiness without blocking;
failed uploads propagate while the preceding scene remains retained. Keep input,
the provider, and dispatch dependencies alive through runtime cleanup.
Add interface components
Construct components after the automatically sized session returns, on simulation.
Pass each a Session& whose lifetime covers its widgets and callbacks. The
controller drives input/time and records the whole GUI; individual components
update their own presentation. Each component should remove only its own widgets
when retired during play. Release all component-held toolkit references before
final session closure.
The next lesson adds Hud to this controller. Menus and chat can use the same
relationship. Keep application choices about visible panels, modal behavior,
pointer selection, and restoring widget focus in the enclosing UI owner. The
session already supplies event capture and routed keyboard/text focus leases.
For complete interactive input handling, follow the
TGUI demo and
focus procedure.
Observe the lifecycle
At this checkpoint the UI has no widgets, so its scene is empty. Inspect the
session and engine_.simulation_view().snapshot() in a debugger during updates.
The session should be created once and follow logical/pixel size changes. Run both runtime modes
to exercise the owner boundary. Continue with Create a HUD for visible
score and health widgets and the completed maintained controller.