For the complete documentation index, see llms.txt. This page is also available as Markdown.

World Space UI

Use world space UI to draw text above players heads and any other text that should feel like it's part of the game world itself.

World space UI is drawn in the 2D world instead of on the screen. Use it for nameplates, health bars, signs, and any text that should feel anchored to game objects.

World space UI uses meters for sizing. If you use screen-space point sizes, your UI will be enormous.

Core Rules

  • Draw world-space player UI from that player's ao_late_update under is_local_or_server().

  • For UI anchored to a moving entity, call UI.begin_world_space_ui(entity) and defer UI.end_world_space_ui() so render interpolation follows that entity.

  • Use UI.push_world_draw_context() only for unanchored world drawing.

  • Use meters for rect sizes and offsets.

  • Push Z for proper depth sorting (commonly use pos.y).

  • Use subrect only for percentage fills (like health bars).

Examples

Displaying a player's level

draw_player_level :: proc(player: Player) {
    UI.begin_world_space_ui(player.entity);
    defer UI.end_world_space_ui();

    pos := player.entity.world_position;
    UI.push_z(pos.y);
    defer UI.pop_z();

    ts := UI.default_text_settings();
    ts.size = 0.30; // World space text size
    ts.halign = .CENTER;
    ts.valign = .CENTER;

    text_pos := pos + v2{0, 1.7};
    rect := Rect{text_pos, text_pos}.grow(0.05, 0.4, 0.05, 0.4);
    UI.text(rect, ts, "Lvl %", {player.level});
}

Player base signs

Health bars

Progress bars

World_Progress_Bar provides a standard world-space bar:

The casts keep the division in floating point, and the clamped denominator prevents division by zero.

For a custom bar whose fill changes over time, keep one Float_Interpolation_Helper per bar:

Tutorial arrows

The entity overload includes the target's interpolation offset:

For a script-owned position that moves, store a Position_Interpolation_Helper and pass its offset:

After snapping an entity to a new position, call entity.mark_teleported() so interpolation does not smear between the old and new positions.

Coordinate Conversion

Tips

  • Keep world UI minimal and readable at distance. If you have a zoomable camera, make sure to adjust text size when you zoom out.

  • Use fit_aspect(texture.get_aspect()) if you draw icons in world space.

  • If text flickers or overlaps, check your Z values and spacing.

Last updated