A UI document is a screen authored as data. You build it in the editor, place it in a scene, and drive it from code through named slots and events. Reference: UI documents; the widget vocabulary is the one from First UI.
Samples: scenes/ui_document_demo in the example game (assets/ui/hud.ui.json, src/entities/ui_document_demo_entity.zig) is the HUD this tutorial builds; scenes/rpg_inventory (assets/ui/inventory.ui.json, src/entities/rpg_inventory_entity.zig) is the bigger one to read next — two panels, tabs, a sprite grid, a details column and a confirm modal, all driven from one component.
1. Create the document
In the asset browser choose Create → New UI Document and name it hud. It opens in the UI editor with a column, a title and a button on the stage. The outline on the left is the tree; the properties on the right belong to whatever is selected.
- Select root, set Justify to
startand Padding to16. - With root selected, pick
progressin Add child and press +. Name ithp_barin the Id field, set Segments to5. - Select
title, set its Text to{callsign}. Braces make it a format; the values come from slots.
2. Declare slots
In the Slots panel below the stage press + twice. Name the first hp (type number, preview value 0.72) and the second callsign (type text, preview Wanderer). The title on the stage now reads the preview values.
Select hp_bar and type hp into its Slot field under Value; set Smooth to 10 so the bar eases. Keep hp between 0 and 1: 0.72 means 72% full. Scrub the preview value in the slots panel: the bar and the title follow.
Give the button an Anim of slide_up and watch it enter. Every edit is undoable; the file saves itself a moment after you stop.
3. Place it in a scene
Drag hud.ui.json from the browser into the viewport, or add a UI Document component to any actor and choose the document. The scene now draws the HUD with its preview values. In JSON that is:
{ "id": "hud", "archetype": "_ui", "components": { "ui": { "document": "asset://./ui/hud" } } }4. Drive it from code (programmer handoff)
Artists can stop after placing and saving the document. Give the gameplay programmer its asset path, slot names and types, and button IDs. The following callback assumes a component with normalized health (0–1), callsign, and a resume() method. Set the button ID to continue to match it.
const hi = @import("hikari_game");
pub fn update(self: *@This(), _: hi.ActorContext, _: *const hi.TickContext) void {
const hud = hi.ui().documentOf(hi.world().find("hud")) orelse return;
hud.setNumber("hp", self.health);
hud.setText("callsign", self.callsign);
var event: hi.ui_types.UiDocumentEvent = .{};
while (hud.pollEvent(&event)) {
if (event.node_hash == hi.ui_types.nodeId("continue") and event.kind == .clicked) self.resume();
}
}The component owns the instance, so there is nothing to close. A document your code owns instead comes from try hi.ui().openDocument(hi.AssetRef.must(.ui_document, "asset://./ui/hud")), is drawn with hud.draw(.{}) inside your update like any other widget, and is closed with hud.close().
5. Lists and localization
A repeat node builds its children once per item. Bind its Count to a slot, give the row's text {index}. {item.name}, and write the items from code with hud.setItem("items", i, "name", .{ .text = name }).
A text node's Message key resolves through the localization domain the document names ("localization": "game"), so hud_no_link draws the current language's text and switches with hi.i18n().setLocale.
Where next
- UI documents — every kind, property and binding form
- User interface — what the widgets themselves do
- Localization — authoring the catalogs the keys come from