UI & Text
The Text and UIRect components work together with Sprite to render text in your game. The TextPlugin automatically converts text content into textures each frame.
Setup
In the scene editor: create an entity → add LocalTransform, Sprite, Text, and UIRect components.
- Text — defines the text content and style (font, size, color, alignment)
- UIRect — defines the layout rectangle (size, anchor, pivot)
- Sprite — used internally by the text system to display the rendered texture
Text Properties
| Property | Type | Default | Description |
|---|---|---|---|
content | string | '' | Text content to display |
fontFamily | string | 'Arial' | Font family name |
fontSize | number | 24 | Font size in pixels |
color | Color | {r:1, g:1, b:1, a:1} | Text color RGBA (0–1) |
align | TextAlign | Left | Horizontal alignment |
verticalAlign | TextVerticalAlign | Top | Vertical alignment |
wordWrap | boolean | true | Enable automatic word wrapping |
overflow | TextOverflow | Visible | Overflow handling mode |
lineHeight | number | 1.2 | Line height multiplier |
UIRect Properties
| Property | Type | Default | Description |
|---|---|---|---|
anchorMin | Vec2 | {0.5, 0.5} | Bottom-left anchor relative to parent (0–1 range) |
anchorMax | Vec2 | {0.5, 0.5} | Top-right anchor relative to parent (0–1 range) |
offsetMin | Vec2 | {0, 0} | Pixel offset from anchorMin |
offsetMax | Vec2 | {0, 0} | Pixel offset from anchorMax |
size | Vec2 | {100, 100} | Width and height (used when anchorMin equals anchorMax) |
pivot | Vec2 | {0.5, 0.5} | Pivot point for rotation and scaling |
When anchorMin equals anchorMax, the element has a fixed size positioned at the anchor point. When they differ, the element stretches between the two anchors and size is computed automatically by UILayoutPlugin.
Enums
TextAlign
| Value | Name | Description |
|---|---|---|
| 0 | Left | Align text to the left edge |
| 1 | Center | Center text horizontally |
| 2 | Right | Align text to the right edge |
TextVerticalAlign
| Value | Name | Description |
|---|---|---|
| 0 | Top | Align text to the top |
| 1 | Middle | Center text vertically |
| 2 | Bottom | Align text to the bottom |
TextOverflow
| Value | Name | Description |
|---|---|---|
| 0 | Visible | Text overflows the rectangle boundary |
| 1 | Clip | Text is clipped at the boundary |
| 2 | Ellipsis | Truncated text shows ”…” |
Modifying Text at Runtime
To update text content or style during gameplay, query the Text component with Mut():
import { defineSystem, addSystem, Query, Mut } from 'esengine';import { Text } from 'esengine';
addSystem(defineSystem( [Query(Mut(Text))], (query) => { for (const [entity, text] of query) { text.content = 'Updated!'; text.fontSize = 32; } }));Example: Dynamic Scoreboard
A common pattern is a score display that updates when a value changes:
import { defineComponent, defineTag, defineSystem, addSystem, Query, Mut } from 'esengine';import { Text } from 'esengine';
const ScoreDisplay = defineTag('ScoreDisplay');
const GameState = defineComponent('GameState', { score: 0,});
addSystem(defineSystem( [Query(Mut(Text), ScoreDisplay), Query(GameState)], (displayQuery, stateQuery) => { let score = 0; for (const [, state] of stateQuery) { score = state.score; } for (const [, text] of displayQuery) { text.content = `Score: ${score}`; } }));In the editor: create a text entity with Text, UIRect, Sprite, LocalTransform, and the ScoreDisplay tag.
UIMask
UIMask clips child entities to the parent entity’s UIRect bounds. Any child that extends beyond the rectangle is visually cropped.
Setup: Add UIMask, UIRect, and LocalTransform to the parent entity. All child entities (and their descendants) will be clipped to this rectangle.
UIMask Properties
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Whether clipping is active |
Nesting
When a child entity also has UIMask, its clip rectangle is the intersection of its own UIRect and the parent’s clip rect. This allows nested scroll areas or layered clipping.
Example: Scrollable List
Create a mask container, then move child entities to scroll:
import { defineSystem, addSystem, Res, Input, Query, Mut, LocalTransform, Children } from 'esengine';import { UIMask } from 'esengine';import { ScrollList } from './components';
addSystem(defineSystem( [Res(Input), Query(Children, UIMask, ScrollList), Query(Mut(LocalTransform))], (input, maskQuery, childQuery) => { for (const [entity, children, mask, scroll] of maskQuery) { const scrollDelta = input.getScrollDelta(); for (const child of children.entities) { const [, transform] = childQuery.get(child); transform.position.y += scrollDelta * 20; } } }));UI Interaction
ESEngine provides a built-in UI interaction system for handling hover, press, and click events on UI elements.
Setup
Add the Interactable component to any UI entity with UIRect and LocalTransform. The UIInteractionPlugin (registered automatically by createWebApp()) performs hit testing each frame and manages interaction state.
Interactable
Marks an entity as interactive.
| Property | Type | Default | Description |
|---|---|---|---|
enabled | boolean | true | Whether interaction is enabled |
Button
Adds a state machine on top of Interactable with optional color transitions.
| Property | Type | Default | Description |
|---|---|---|---|
state | ButtonState | Normal | Current button state (read-only at runtime) |
transition | ButtonTransition | null | null | Color transition configuration |
ButtonState: Normal (0), Hovered (1), Pressed (2), Disabled (3)
When transition is set, the Sprite color is automatically updated on state change:
import { Button, Interactable } from 'esengine';
world.insert(entity, Button, { state: 0, transition: { normalColor: { r: 1, g: 1, b: 1, a: 1 }, hoveredColor: { r: 0.9, g: 0.9, b: 0.9, a: 1 }, pressedColor: { r: 0.7, g: 0.7, b: 0.7, a: 1 }, disabledColor: { r: 0.5, g: 0.5, b: 0.5, a: 0.5 }, },});UIInteraction
Automatically added by the plugin to entities with Interactable. Provides per-entity state:
| Property | Type | Description |
|---|---|---|
hovered | boolean | Pointer is over this entity |
pressed | boolean | Entity is being pressed |
justPressed | boolean | Press started this frame |
justReleased | boolean | Press released this frame |
UIEvents
The UIEvents resource collects interaction events each frame. Query it in systems:
import { defineSystem, addSystem, Res } from 'esengine';import { UIEvents } from 'esengine';
addSystem(defineSystem( [Res(UIEvents)], (events) => { for (const e of events.query('click')) { console.log('Clicked entity:', e.entity); } }));Event types: click, press, release, hover_enter, hover_exit
ScreenSpace
A tag component for root UI entities. Entities with ScreenSpace, UIRect, and LocalTransform are laid out relative to the camera bounds by UILayoutPlugin.
import { ScreenSpace, UIRect, LocalTransform } from 'esengine';
world.insert(entity, ScreenSpace);world.insert(entity, UIRect, { anchorMin: { x: 0, y: 0 }, anchorMax: { x: 1, y: 1 }, offsetMin: { x: 0, y: 0 }, offsetMax: { x: 0, y: 0 }, size: { x: 100, y: 100 }, pivot: { x: 0.5, y: 0.5 },});Next Steps
- Components — all builtin components
- Sprite — sprite rendering details
- Bitmap Text — GPU-rendered bitmap font text
- Asset Loading — loading textures and other assets