Text: text2d, text3d and the shaper
The text2d and text3d components: keys, outline and shadow, markup, script calls, measuring, bitmap fonts and what a block costs.
Sur cette page
text2d and text3d draw text in the scene. The widget layer draws text on
the screen.
One shaper and one glyph atlas serve both: bidirectional text, Arabic joining, CJK and Thai line breaking and the project's font chain work the same in a panel and in the world.

Two components
text2d draws in the 2D pass, sized like a sprite. text3d draws in the 3D
pass on a quad facing the camera. They take the same keys.
[[nodes]]
id = "sign"
name = "Sign"
[nodes.transform]
position = [0.0, 1.6, 0.0]
[nodes.text3d]
text = "A score on a sign"
font_size = 64
color = [1.0, 0.85, 0.3, 1.0]
pixels_per_unit = 110
outline_size = 3.0
outline_color = [0.1, 0.05, 0.0, 1.0]
| Key | What it does |
|---|---|
text |
The string drawn |
text_key |
A key in the project's strings, re-read every frame |
font_family |
Which font chain to shape with: ui, heading, mono or icon |
bitmap_font |
A project-relative .fnt naming a bitmap face |
font_size, font_weight, font_style |
Height in font pixels, 100–900, upright or italic |
color, text_align |
Tint, and where the block sits across the node's origin |
max_width, line_height, letter_spacing |
Wrapping width, baseline spacing, tracking |
markup |
Read the text as markup |
outline_size, outline_color |
An outline around the glyphs |
shadow_offset_x, shadow_offset_y, shadow_color |
A shadow behind them |
pixels_per_unit |
Font pixels to one world unit |
- Doubling
font_sizeandpixels_per_unittogether keeps the on-screen size and doubles the sharpness. text3daddsbillboard,double_sided,depth_testandalpha_cut.billboardturns the block to the camera every frame. Off, the block stays in the node's own plane.
Markup
With markup = true the text takes the same tags as a widget's label: [b],
[i], [color=#7fd4ff], [wave amp= freq=], [center], [right] and an
inline [img=…]. A coloured run keeps its own colour over the block's.
Two more tags answer the pointer, which only a widget label can do:
[url=target] calls that label's on_link with the target, and
[hint=text] shows the text while the pointer rests on the span.
From a script
let node = scene::get_node("Hud/Score");
node.text2d.text = format!("{}", this.score);
render::draw_text_3d(x, y, z, "over a character", #{ font_size: 32.0, text_align: "center" });
render::draw_text_2d(x, y, "in the 2D pass", #{ font_size: 24.0 });
text2d.textandtext3d.textchange what a node draws.draw_text_2danddraw_text_3ddraw with no node, for one frame. Neither is recorded: a replay re-runs the script that drew.- The editor's Rig tool labels bones with
draw_text_3d.
Measuring
let (w, h) = render::text_size("measure me", #{ font_size: 32.0 });
- Returns the width and height a string shapes to, in font pixels.
- Measured against the project's fonts and the bundled ones, never the machine's. Every platform answers the same.
- A width is presentation. A script that writes one into state puts
presentation in the digest. The same holds for a
kind = "text"mesh, whose vertices are geometry with colliders fitted to them.
Bitmap fonts
[nodes.text2d]
text = "SCORE 1200"
bitmap_font = "fonts/pixel.fnt"
font_size = 32
bitmap_fontnames an AngelCode.fnt. The engine reads its page and every glyph's box, then lays the text out by advance and kerning.- Nothing is shaped: a bitmap font has one glyph per character and no contextual forms.
- The page is copied into the same atlas as the rasterised glyphs. A bitmap glyph and a vector one are one texture and one draw.
Editing in the viewport
Double-click a text2d or text3d in the Scene workspace to open a field over
it. Enter keeps the text, Escape drops it.
Cost
- A block is one mesh per colour it draws in, plus one for the outline and one for the shadow.
- Glyphs are rasterised at the size the block covers on screen, quantized to buckets 8.7% apart.
- Walking towards a sign re-shapes it a few times. Two blocks at nearly the same size share their glyphs.
- A glyph is scaled by up to 8.7% between buckets.
examples/text
has all of it.