Runtime UI is immediate-mode: each frame you rebuild widgets through the bound UI facade (const ui = hi.ui();). Nest controls in stacks (ui.vStack / ui.hStack) and always defer ui.end(scope).
Deep reference: User interface, UI layout, UI widgets, UI and editor, Motion Kit.
Choose an owner
| UI kind | Owner | Lifetime |
|---|---|---|
| Screen / level-specific panels | Scene entity update | Dies with the scene |
| Cross-scene HUD / pause / settings chrome | GameSubsystem.onTick | Survives scene replace |
Do not hang tween timelines on UiContext — store drivers on entity or subsystem state (hi.motion).
Scene-owned UI entity
const hi = @import("hikari_game");
const HudLogic = hi.defineComponent(.{
.name = "hud_logic",
.storage = .embedded,
.data = struct {
pub fn update(_: *@This(), _: hi.ActorContext, _: *const hi.TickContext) void {
const ui = hi.ui();
const panel = ui.vStack(.{
.position = .{ 16, 16 },
.width = .{ .points = 240 },
.padding = 12,
.spacing = 8,
.surface = .surface,
});
defer ui.end(panel);
ui.text("Scene HUD", .{ .role = .secondary });
if (ui.button("Reload", .{
.id = "scene-hud.reload",
.variant = .primary,
})) {
hi.world().requestSceneReload();
}
}
},
});
pub const HudEntity = hi.defineActor(.{
.archetype = "scene_hud",
.components = .{HudLogic},
});Place "archetype": "scene_hud" in the scene (no render/physics required). Rebuild, Play, and click the button.
Stable .id values matter when labels repeat — hit-testing keys off the id.
Sample overlays: session HUD profiler / G-buffer toggles in src/games/example/src/session_ui.zig (settings sheet). Scene-placed debug (debug_entity.zig) is an empty marker.
Session HUD
From GameSubsystem.onWorldAttach, reserve capacity for each World:
const ui = hi.ui();
ui.reserveCapacity(64 * 1024, 64, 48);
ui.setDensity(.compact);From onTick, draw the same stack pattern with hi.ui(). Full sample: src/games/example/src/session.zig — compact session card plus an optional non-modal settings sheet (graphics knobs, keybind legend) so the scene stays visible while tuning.
Modals: finish the page stack, then open beginModal at the root (see sample quit confirm). Prefer a second positioned vStack for settings/tools that should not dim the world.
Layout tips
- Root stacks default near
{16, 16}; use.width = .{ .percent = 0.30 }withmin_width/max_widthfor responsive panels. justify/ flex reflow uses previous-frame geometry for hit-testing — expect a one-frame lag when a layout first appears.- Game theme is
Theme.runtime(); do not copy the editor retained theme onto the runtime UI context.
Verify
- Play with a scene HUD entity or the sample session panel.
- Confirm entity UI clicks work only while Play (Edit freezes entity updates;
GameSubsystem.onTickstill runs in editor Play). - Replace the scene — session HUD should remain; scene entity UI should disappear.