text
The text namespace — 49 functions.
globals/text/alive
text.alive(handle: any?) -> boolean
Whether the text system still holds this handle — true between
text.create and the text.destroy that released it.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns boolean — True while the handle is live.
if not text.alive(h) then h = text.create({ content = "again" }) end
globals/text/count
text.count() -> number
How many text objects the text system is holding — the number that
moves when text.create and text.destroy are called.
Returns number — The live text-object count.
local before = text.count()
globals/text/create
text.create(options: table) -> any
Create a text handle from an initial content + style table. The handle
owns a runtime GPU texture (see text.textureGuid); pass it to every other
call.
Parameters
optionstable— Table ofcontentplus style fields (fontSize, color, alignment, richText, maxWidth, ...).
Returns any — An opaque text handle, or nil if the text system is unavailable.
local h = text.create({ content = "Hello", fontSize = 48 })
globals/text/destroy
text.destroy(handle: any?) -> boolean
Destroy a text handle and release its raster + glyph layout.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns boolean — True when the text system held the handle and released it; false for a handle it did not have.
text.destroy(h)
globals/text/face
text.face(handle: any?) -> any
Which font face one handle actually shaped with, and whether that is
the family its style asked for. requested is what was asked, resolved
is the face that answered, matched says whether they agree and reason
says why when they do not — one of text.faceReasons(). A style that named
no family reports noFamilyRequested: it got the default because it asked
for nothing, so reason rather than matched is what an alert switches
on. faces lists
every face the shaper used, most glyphs first, so a fallback that covered
part of the string is visible alongside the face that covered the rest.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns any — { requested, resolved, postScriptName, matched, reason, faces, glyphCount }, or nil for a handle the text system does not hold.
local r = text.face(h).reason; if r == "familyUnknown" or r == "familyNotSelectable" then print(r) end
globals/text/faceReasons
text.faceReasons() -> { string }
Every reason the face readings give for a label or a family not being
in the family a style named, nearest cause first. text.face gives them
for one label; font.reconcile() also gives familyCoveredNoGlyph, which
it can only reach by laying the family out under its own weights and over
several scripts.
Returns { string } — Array of reason strings.
for _, r in ipairs(text.faceReasons()) do print(r) end
globals/text/listFonts
text.listFonts() -> { string }
List the font families currently available to the text system.
Returns { string } — Array of font-family name strings.
local fonts = text.listFonts()
globals/text/loadFont
text.loadFont(ref: any?) -> any
Load a font from an asset reference so it becomes available to
setStyle's fontFamily.
Parameters
refany(optional) — Font asset reference or path.
Returns any — The loaded font-family name, or nil on failure.
text.loadFont(asset.ref("fonts.inter", "font"))
globals/text/measure
text.measure(handle: any?) -> any
Measure the rasterised text in pixels without producing a texture.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns any — Table with width and height in pixels.
local size = text.measure(h)
globals/text/observe
text.observe() -> any
Everything the text system is holding right now. count is the live
text objects; objects is one row each, carrying its content, the style
it was laid out with, its measured extent, whether it is dirty, the face
the shaper actually used, the owner entity whose component created it
with whether that entity is still there, and the raster texture its last
rasterisation landed in with the bytes it costs. orphans is the subset
whose owning entity is gone, fonts the families the shaper can resolve,
and raster the glyph-raster bytes with the pool they belong to named.
Built when you ask, so it costs nothing per frame and reads the same in
edit mode as in play.
Returns any — { count, objects, orphans, fonts, dirty, raster }.
local live = text.observe().count
globals/text/orphans
text.orphans() -> { any }
The text objects whose owning entity no longer exists — a quad the
engine is still holding for something that has been despawned. Each row is
the same shape text.observe().objects carries.
Returns { any } — Array of text-object rows with a dead owner.
print(#text.orphans() .. " labels outlived their entity")
globals/text/rasterMemory
text.rasterMemory() -> any
The glyph-raster bytes, broken out of the runtime GPU texture pool.
bytes is summed off the same map renderer.gpuMemory().textures is
totalled from, so shareOfPool is a share of that number rather than a
second count of the same memory.
Returns any — { pool, bytes, textures, poolBytes, shareOfPool }.
local r = text.rasterMemory(); print(r.bytes .. " of " .. r.poolBytes)
globals/text/rasterize
text.rasterize(handle: any?, texture: any?, scale: number?) -> any
Rasterise the handle's current text + style into the given runtime GPU
texture. Bind that texture's guid as a material's base_color_texture to
display the text; re-rasterising the same texture overwrites it in place.
Parameters
handleany(optional) — Text handle fromtext.create.textureany(optional) — Destination GPU texture handle (renderer.texture.create) or its guid string — WHERE the raster lands.scalenumber(optional) — World/pixel scale factor for the raster (default 1.0).
Returns any — Table with width and height (in pixels), or nil if nothing rasterised.
local tex = renderer.texture.create({ width = 256, height = 64 })
local r = text.rasterize(h, tex, 1.0)
globals/text/setStyle
text.setStyle(handle: any?, style: table) -> boolean
Replace the handle's style. Fields not present keep their current value.
Parameters
handleany(optional) — Text handle fromtext.create.styletable— Style table (fontSize, color, alignment, outline, ...).
Returns boolean — True when the text system held the handle and took the style; false when it did not.
text.setStyle(h, { fontSize = 64, color = "yellow" })
globals/text/setText
text.setText(handle: any?, content: string) -> boolean
Replace the handle's text content.
Parameters
handleany(optional) — Text handle fromtext.create.contentstring— New text string.
Returns boolean — True when the text system held the handle and took the content; false when it did not, which is how a caller learns its handle went away.
if not text.setText(h, "HP: 100") then h = text.create({ content = "HP: 100" }) end
globals/text/textureGuid
text.textureGuid(handle: any?) -> string?
The runtime GPU texture guid this handle rasterises into — bind it as a
material texture (base_color_texture) to display the text.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns string? — The texture guid string, or nil for a handle the text system does not hold.
entity(id).component.get("Material"):setTexture("base_color_texture", text.textureGuid(h))
modules/text/README
require("@builtin/modules/api/engine/text") -- text (also available as global 'text')
Text rasterisation resource — create a text handle, set its content and style, then rasterise it to a texture for display, and observe what the text system is holding. Public Luau surface over the __text and __textObserve Internal FFI namespaces.
Usage: local text = require("@builtin/modules/api/engine/text") Also available as global: text
modules/text/alive
alive(handle: any): boolean
Whether the text system still holds this handle — true between
text.create and the text.destroy that released it.
Parameters
handleany(optional) — Text handle fromtext.create.
if not text.alive(h) then h = text.create({ content = "again" }) end
modules/text/count
count(): number
How many text objects the text system is holding — the number that
moves when text.create and text.destroy are called.
local before = text.count()
modules/text/create
create(options: table): any
Create a text handle from an initial content + style table. The handle
owns a runtime GPU texture (see text.textureGuid); pass it to every other
call.
Parameters
optionstable— Table ofcontentplus style fields (fontSize, color, alignment, richText, maxWidth, ...).
local h = text.create({ content = "Hello", fontSize = 48 })
modules/text/destroy
destroy(handle: any): boolean
Destroy a text handle and release its raster + glyph layout.
Parameters
handleany(optional) — Text handle fromtext.create.
text.destroy(h)
modules/text/face
face(handle: any): any
Which font face one handle actually shaped with, and whether that is
the family its style asked for. requested is what was asked, resolved
is the face that answered, matched says whether they agree and reason
says why when they do not — one of text.faceReasons(). A style that named
no family reports noFamilyRequested: it got the default because it asked
for nothing, so reason rather than matched is what an alert switches
on. faces lists
every face the shaper used, most glyphs first, so a fallback that covered
part of the string is visible alongside the face that covered the rest.
Parameters
handleany(optional) — Text handle fromtext.create.
local r = text.face(h).reason; if r == "familyUnknown" or r == "familyNotSelectable" then print(r) end
modules/text/faceReasons
faceReasons(): { string }
Every reason the face readings give for a label or a family not being
in the family a style named, nearest cause first. text.face gives them
for one label; font.reconcile() also gives familyCoveredNoGlyph, which
it can only reach by laying the family out under its own weights and over
several scripts.
for _, r in ipairs(text.faceReasons()) do print(r) end
modules/text/listFonts
listFonts(): { string }
List the font families currently available to the text system.
local fonts = text.listFonts()
modules/text/loadFont
loadFont(ref: any): any
Load a font from an asset reference so it becomes available to
setStyle's fontFamily.
Parameters
refany(optional) — Font asset reference or path.
text.loadFont(asset.ref("fonts.inter", "font"))
modules/text/measure
measure(handle: any): any
Measure the rasterised text in pixels without producing a texture.
Parameters
handleany(optional) — Text handle fromtext.create.
local size = text.measure(h)
modules/text/observe
observe(): any
Everything the text system is holding right now. count is the live
text objects; objects is one row each, carrying its content, the style
it was laid out with, its measured extent, whether it is dirty, the face
the shaper actually used, the owner entity whose component created it
with whether that entity is still there, and the raster texture its last
rasterisation landed in with the bytes it costs. orphans is the subset
whose owning entity is gone, fonts the families the shaper can resolve,
and raster the glyph-raster bytes with the pool they belong to named.
Built when you ask, so it costs nothing per frame and reads the same in
edit mode as in play.
local live = text.observe().count
modules/text/orphans
orphans(): { any }
The text objects whose owning entity no longer exists — a quad the
engine is still holding for something that has been despawned. Each row is
the same shape text.observe().objects carries.
print(#text.orphans() .. " labels outlived their entity")
modules/text/rasterMemory
rasterMemory(): any
The glyph-raster bytes, broken out of the runtime GPU texture pool.
bytes is summed off the same map renderer.gpuMemory().textures is
totalled from, so shareOfPool is a share of that number rather than a
second count of the same memory.
local r = text.rasterMemory(); print(r.bytes .. " of " .. r.poolBytes)
modules/text/rasterize
rasterize(handle: any, texture: any, scale: number?): any
Rasterise the handle's current text + style into the given runtime GPU
texture. Bind that texture's guid as a material's base_color_texture to
display the text; re-rasterising the same texture overwrites it in place.
Parameters
handleany(optional) — Text handle fromtext.create.textureany(optional) — Destination GPU texture handle (renderer.texture.create) or its guid string — WHERE the raster lands.scalenumber?(optional) — World/pixel scale factor for the raster (default 1.0).
local tex = renderer.texture.create({ width = 256, height = 64 })
local r = text.rasterize(h, tex, 1.0)
modules/text/setStyle
setStyle(handle: any, style: table): boolean
Replace the handle's style. Fields not present keep their current value.
Parameters
handleany(optional) — Text handle fromtext.create.styletable— Style table (fontSize, color, alignment, outline, ...).
text.setStyle(h, { fontSize = 64, color = "yellow" })
modules/text/setText
setText(handle: any, content: string): boolean
Replace the handle's text content.
Parameters
handleany(optional) — Text handle fromtext.create.contentstring— New text string.
if not text.setText(h, "HP: 100") then h = text.create({ content = "HP: 100" }) end
modules/text/textureGuid
textureGuid(handle: any): string?
The runtime GPU texture guid this handle rasterises into — bind it as a
material texture (base_color_texture) to display the text.
Parameters
handleany(optional) — Text handle fromtext.create.
entity(id).component.get("Material"):setTexture("base_color_texture", text.textureGuid(h))
typed/builtin//modules/api/engine/text/text/alive
text.alive(handle: any?) -> boolean
Whether the text system still holds this handle — true between
text.create and the text.destroy that released it.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns boolean — True while the handle is live.
if not text.alive(h) then h = text.create({ content = "again" }) end
typed/builtin//modules/api/engine/text/text/count
text.count() -> number
How many text objects the text system is holding — the number that
moves when text.create and text.destroy are called.
Returns number — The live text-object count.
local before = text.count()
typed/builtin//modules/api/engine/text/text/create
text.create(options: table) -> any
Create a text handle from an initial content + style table. The handle
owns a runtime GPU texture (see text.textureGuid); pass it to every other
call.
typed/builtin//modules/api/engine/text/text/destroy
text.destroy(handle: any?) -> boolean
Destroy a text handle and release its raster + glyph layout.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns boolean — True when the text system held the handle and released it; false for a handle it did not have.
text.destroy(h)
typed/builtin//modules/api/engine/text/text/face
text.face(handle: any?) -> any
Which font face one handle actually shaped with, and whether that is
the family its style asked for. requested is what was asked, resolved
is the face that answered, matched says whether they agree and reason
says why when they do not — one of text.faceReasons(). A style that named
no family reports noFamilyRequested: it got the default because it asked
for nothing, so reason rather than matched is what an alert switches
on. faces lists
every face the shaper used, most glyphs first, so a fallback that covered
part of the string is visible alongside the face that covered the rest.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns any — { requested, resolved, postScriptName, matched, reason, faces, glyphCount }, or nil for a handle the text system does not hold.
local r = text.face(h).reason; if r == "familyUnknown" or r == "familyNotSelectable" then print(r) end
typed/builtin//modules/api/engine/text/text/faceReasons
text.faceReasons() -> { string }
Every reason the face readings give for a label or a family not being
in the family a style named, nearest cause first. text.face gives them
for one label; font.reconcile() also gives familyCoveredNoGlyph, which
it can only reach by laying the family out under its own weights and over
several scripts.
Returns { string } — Array of reason strings.
for _, r in ipairs(text.faceReasons()) do print(r) end
typed/builtin//modules/api/engine/text/text/listFonts
text.listFonts() -> { string }
List the font families currently available to the text system.
Returns { string } — Array of font-family name strings.
local fonts = text.listFonts()
typed/builtin//modules/api/engine/text/text/loadFont
text.loadFont(ref: any?) -> any
Load a font from an asset reference so it becomes available to
setStyle's fontFamily.
Parameters
refany(optional) — Font asset reference or path.
Returns any — The loaded font-family name, or nil on failure.
text.loadFont(asset.ref("fonts.inter", "font"))
typed/builtin//modules/api/engine/text/text/measure
text.measure(handle: any?) -> any
Measure the rasterised text in pixels without producing a texture.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns any — Table with width and height in pixels.
local size = text.measure(h)
typed/builtin//modules/api/engine/text/text/observe
text.observe() -> any
Everything the text system is holding right now. count is the live
text objects; objects is one row each, carrying its content, the style
it was laid out with, its measured extent, whether it is dirty, the face
the shaper actually used, the owner entity whose component created it
with whether that entity is still there, and the raster texture its last
rasterisation landed in with the bytes it costs. orphans is the subset
whose owning entity is gone, fonts the families the shaper can resolve,
and raster the glyph-raster bytes with the pool they belong to named.
Built when you ask, so it costs nothing per frame and reads the same in
edit mode as in play.
Returns any — { count, objects, orphans, fonts, dirty, raster }.
local live = text.observe().count
typed/builtin//modules/api/engine/text/text/orphans
text.orphans() -> { any }
The text objects whose owning entity no longer exists — a quad the
engine is still holding for something that has been despawned. Each row is
the same shape text.observe().objects carries.
Returns { any } — Array of text-object rows with a dead owner.
print(#text.orphans() .. " labels outlived their entity")
typed/builtin//modules/api/engine/text/text/rasterMemory
text.rasterMemory() -> any
The glyph-raster bytes, broken out of the runtime GPU texture pool.
bytes is summed off the same map renderer.gpuMemory().textures is
totalled from, so shareOfPool is a share of that number rather than a
second count of the same memory.
Returns any — { pool, bytes, textures, poolBytes, shareOfPool }.
local r = text.rasterMemory(); print(r.bytes .. " of " .. r.poolBytes)
typed/builtin//modules/api/engine/text/text/rasterize
text.rasterize(handle: any?, texture: any?, scale: number?) -> any
Rasterise the handle's current text + style into the given runtime GPU
texture. Bind that texture's guid as a material's base_color_texture to
display the text; re-rasterising the same texture overwrites it in place.
Parameters
handleany(optional) — Text handle fromtext.create.textureany(optional) — Destination GPU texture handle (renderer.texture.create) or its guid string — WHERE the raster lands.scalenumber(optional) — World/pixel scale factor for the raster (default 1.0).
Returns any — Table with width and height (in pixels), or nil if nothing rasterised.
local tex = renderer.texture.create({ width = 256, height = 64 })
local r = text.rasterize(h, tex, 1.0)
typed/builtin//modules/api/engine/text/text/setStyle
text.setStyle(handle: any?, style: table) -> boolean
Replace the handle's style. Fields not present keep their current value.
Parameters
handleany(optional) — Text handle fromtext.create.styletable— Style table (fontSize, color, alignment, outline, ...).
Returns boolean — True when the text system held the handle and took the style; false when it did not.
text.setStyle(h, { fontSize = 64, color = "yellow" })
typed/builtin//modules/api/engine/text/text/setText
text.setText(handle: any?, content: string) -> boolean
Replace the handle's text content.
Parameters
handleany(optional) — Text handle fromtext.create.contentstring— New text string.
Returns boolean — True when the text system held the handle and took the content; false when it did not, which is how a caller learns its handle went away.
if not text.setText(h, "HP: 100") then h = text.create({ content = "HP: 100" }) end
typed/builtin//modules/api/engine/text/text/textureGuid
text.textureGuid(handle: any?) -> string?
The runtime GPU texture guid this handle rasterises into — bind it as a
material texture (base_color_texture) to display the text.
Parameters
handleany(optional) — Text handle fromtext.create.
Returns string? — The texture guid string, or nil for a handle the text system does not hold.
entity(id).component.get("Material"):setTexture("base_color_texture", text.textureGuid(h))