UI HUD panels widgets and world nameplates
TextRenderableComponent.Use this for
Not for
Pairs with: Game loop assembly entities scripts HUD win shippable loop, Saves persistence player progress and data stores, Collectibles pickups scoring and win condition, Combat damage health and respawn scripting
Wire the whole HUD in one pass
Create the panel ONCE, then bind every widget in a SINGLE project_apply_script_semantic_patch pass rather than one call per widget. Each wireHudStateBinding op adds one widget-to-state binding; put them all in the same patch so the HUD is wired as one decisive edit, not a drip of updates. A single field update to one widget is the precision tool, for a targeted repair.
The recipe
Screen HUD:
- Create a panel with
project_create_ui_panel(project.ui_panel.create) holding the widgets you want (a score readout, a bar). - Bind a widget to durable state from a script with
project_apply_script_semantic_patchopwireHudStateBinding{ "scope":{"kind":"handler","handlerRef":<key>}, "panelKey": <panel key>, "widgetKey": <widget key>, "storeKey":"progress", "recordKey":"player", "field":"score", "valueType":"number", "defaultValue": 0 }. It reads the warehouse field and pushes it throughctx.ui.setWidgetValue. Add one such op per widget in the same patch. - Open and close panels from a GessaScript body with
ctx.ui.openPanelandctx.ui.closePanel(for a pause menu, a shop, a death screen).
World-space labels:
A player's own name over their character is NOT a TextRenderableComponent. The game's character policy nameplate setting (everyone, team, or none) makes the runtime stamp a server-written NameplateComponent from the account, so you never author a player name plate by hand (see player-characters-and-spawns). Use TextRenderableComponent for non-player world labels: enemy nameplates, item names, damage numbers, and quest markers.
- Attach
TextRenderableComponentto the labeled entity{ "type":"TextRenderableComponent", "enabled": true, "text":"Enemy", "textSource":"static", "color":"#ffffff", "fontSize": 18, "anchor":"bottom", "billboardMode":"camera", "offset":{"x":0,"y":1.8,"z":0}, "maxDistance": 80, "fadeDistance": 60 }. - Drive the label from live data with
textSource:static(fixedtext),entityName(the entity's name),projection(aprojectionKey), orstat(astatKey). Usestatfor a floating value that updates from state.
Pitfalls
wireHudStateBindingreads the SAME store/record/field a save uses; wire the HUD to the field you already persist (see saves-and-persistence), not a separate copy.TextRenderableComponent.textis capped at 256 chars;fontSizeis 6..96;fontFamilyissans,mono, orserif. Out-of-range values fail.- A
TextRenderableComponentwithtextSource:"stat"needs astatKey; withprojectionit needs aprojectionKey. The discriminator drives which field is required. - HUD widget values are a
uiEffect; they display state, they do not persist it.
Verify
engine_get_component_schemaonTextRenderableComponentfor text-source enums and bounds.qa_capture_renderer_viewport(qa.renderer.viewport.capture) to confirm the HUD and nameplates render.simulation_runto confirm the bound widget updates when the state changes.