Skip to content

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

PropertyTypeDefaultDescription
contentstring''Text content to display
fontFamilystring'Arial'Font family name
fontSizenumber24Font size in pixels
colorColor{r:1, g:1, b:1, a:1}Text color RGBA (0–1)
alignTextAlignLeftHorizontal alignment
verticalAlignTextVerticalAlignTopVertical alignment
wordWrapbooleantrueEnable automatic word wrapping
overflowTextOverflowVisibleOverflow handling mode
lineHeightnumber1.2Line height multiplier

UIRect Properties

PropertyTypeDefaultDescription
anchorMinVec2{0.5, 0.5}Bottom-left anchor relative to parent (0–1 range)
anchorMaxVec2{0.5, 0.5}Top-right anchor relative to parent (0–1 range)
offsetMinVec2{0, 0}Pixel offset from anchorMin
offsetMaxVec2{0, 0}Pixel offset from anchorMax
sizeVec2{100, 100}Width and height (used when anchorMin equals anchorMax)
pivotVec2{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

ValueNameDescription
0LeftAlign text to the left edge
1CenterCenter text horizontally
2RightAlign text to the right edge

TextVerticalAlign

ValueNameDescription
0TopAlign text to the top
1MiddleCenter text vertically
2BottomAlign text to the bottom

TextOverflow

ValueNameDescription
0VisibleText overflows the rectangle boundary
1ClipText is clipped at the boundary
2EllipsisTruncated 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

PropertyTypeDefaultDescription
enabledbooleantrueWhether 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.

PropertyTypeDefaultDescription
enabledbooleantrueWhether interaction is enabled

Button

Adds a state machine on top of Interactable with optional color transitions.

PropertyTypeDefaultDescription
stateButtonStateNormalCurrent button state (read-only at runtime)
transitionButtonTransition | nullnullColor 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:

PropertyTypeDescription
hoveredbooleanPointer is over this entity
pressedbooleanEntity is being pressed
justPressedbooleanPress started this frame
justReleasedbooleanPress 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