Skip to main content

UI and Canvas

Polygon Engine UI is authored as named Canvases containing Panel, Text, Image, Button, and Progress Bar elements. Use typed tables to change only the property owned by that element or component. UI.DrawText remains available for transient diagnostics.

Lookup​

UI.Find(name) returns a scene-global element ID or -1. Canvas.Find(name) returns a Canvas ID or -1. Cache both IDs in Start.

UI.GetCount() and Canvas.GetCount() report authored counts. UI.GetType(elementId) returns the element type code or nil for a missing ID:

CodeElement
0Panel
1Button
2Text
3Image
4Progress Bar

Canvas Visibility and Order​

MethodPurpose
Canvas.SetEnabled(canvasId, enabled)Shows or hides one Canvas.
Canvas.IsEnabled(canvasId)Reads Canvas visibility.
Canvas.SetSortOrder(canvasId, sortOrder)Changes draw order in the validated range.

An Interactable Prompt Canvas also has transient range visibility. That gate does not overwrite the enabled value set through Canvas.SetEnabled.

Element APIs​

TableMethods
TextText.SetText(elementId, text), Text.GetText(elementId), Text.SetVisibleCharacterCount(elementId, count), Text.GetVisibleCharacterCount(elementId), Text.SetColor(elementId, r, g, b[, a]), Text.GetColor(elementId)
ImageImage.SetTexture(elementId, textureGuid), Image.GetTexture(elementId)
ButtonButton.SetInteractable(elementId, interactable), Button.IsInteractable(elementId)
RectTransformRectTransform.SetPosition(elementId, x, y), RectTransform.GetPosition(elementId), RectTransform.SetSize(elementId, width, height), RectTransform.GetSize(elementId), RectTransform.SetScale(elementId, x, y), RectTransform.GetScale(elementId)
GraphicGraphic.SetColor(elementId, r, g, b[, a]), Graphic.GetColor(elementId), Graphic.SetEnabled(elementId, enabled), Graphic.IsEnabled(elementId)
SelectableSelectable.Select(elementId), Selectable.GetSelected()
ProgressBarProgressBar.SetValue(elementId, value), ProgressBar.GetValue(elementId)

Positions and sizes return two scalar values. Colors use 8-bit RGBA channels; alpha defaults to 255. Progress values are clamped to 0.0..1.0. Image texture changes use the imported texture GUID, not a source filename.

Image.SetTexture and Image.GetTexture also address textured Buttons and Panels in Play Mode and PS1 builds. Use them on texture-backed elements, not Text. For a missing element the setter returns false and the getter nil. On PS1 the setter also returns false for an empty GUID or a texture that is not already loaded. An assignment in Play Mode does not prove that the texture is available on disc. Reference every candidate texture in the scene's authored UI, including initially disabled elements, so the build includes it. Hidden UI still counts toward element, texture and VRAM budgets. Use Graphic.SetEnabled to hide an empty inspection image; an empty slot should not retain the previous item's picture.

local hudCanvasId = -1
local statusTextId = -1
local healthBarId = -1
local resumeButtonId = -1

function Start(gameObjectId)
hudCanvasId = Canvas.Find("HUD")
statusTextId = UI.Find("Status Text")
healthBarId = UI.Find("Health")
resumeButtonId = UI.Find("Resume Button")

Canvas.SetEnabled(hudCanvasId, true)
Text.SetText(statusTextId, "READY")
ProgressBar.SetValue(healthBarId, 1.0)
Selectable.Select(resumeButtonId)
end

function SetHealth(gameObjectId, currentHealth, maximumHealth)
local value = maximumHealth > 0 and currentHealth / maximumHealth or 0.0
ProgressBar.SetValue(healthBarId, value)
end

Controller Navigation and Buttons​

Buttons use authored automatic or explicit navigation. Selectable.Select moves focus, while Button.SetInteractable removes or restores a Button as a valid selection and submit target.

On submit, the configured function runs on the target Lua Behaviour with:

function OpenLevel(gameObjectId, elementId, elementName, eventName)
if eventName == "click" then
SceneManager.LoadScene("Scenes/level.json")
end
end

The first argument is the target GameObject ID, followed by the Button element ID, element name, and "click". The authored Function field must match exactly. Keep callbacks small and move longer flows into normal script state updated by Update.

Immediate Debug Text​

UI.DrawText(x, y, text) queues text for one frame:

function Update(gameObjectId, deltaTime)
UI.DrawText(8, 8, "Debug build")
end

Use authored Text for shipping HUDs and menus so layout, fonts, visibility, and focus remain consistent.

PS1 Limits​

One scene supports up to eight Canvases, 32 authored UI elements across them, and three bitmap fonts. Build validation also checks font glyph grids against the imported atlas dimensions. Keep hierarchy and text short, reuse Panels, and test controller navigation at 320x240.

See UI Canvas for authoring and bitmap fonts.

Progressive text and visual scale​

Text.SetVisibleCharacterCount reveals a prefix without changing wrapping or alignment. Use -1 for the entire text, 0 for no glyphs, or 1..127 for a byte prefix. Fonts are byte atlases: spaces and newlines count, and Unicode shaping is not provided. The setter accepts Text and Button elements and returns false for an invalid ID, another element type, or a count outside -1..127. Keep each PS1 label/page within 127 bytes. GetVisibleCharacterCount reads the current limit. SetColor / GetColor on Text control the text tint independently of Graphic's background.

local label = UI.Find("Dialogue Text")
Text.SetText(label, "The full sentence stays in the layout.")
Text.SetVisibleCharacterCount(label, 0)
Text.SetVisibleCharacterCount(label, 12)
Text.SetVisibleCharacterCount(label, -1)
Text.SetColor(label, 223, 222, 194, 255)
RectTransform.SetScale(UI.Find("Dialogue Panel"), .96, .96)

RectTransform.SetScale / GetScale expose a local X/Y transform around the pivot with a 0..4 range, inherited by descendants and navigation geometry. Zero scale removes that hierarchy from focus. Use imported bitmap fonts for scaled text; the legacy debug-font fallback retains its fixed glyph size. PS1 transparency uses hardware blending, so RGB tint and motion are preferable to smooth alpha.

Last Exit's dialogue.lua uses unscaled time, punctuation pauses and an explicit entry/typing/wait/change/exit sequence. Cross finishes the letters before a second press advances or confirms; Circle cancels. Controls > Square toggles typewriter/instant text. All game decisions remain in flow.lua.

A typewriter timer​

Author an enabled Text element named Dialogue Text with a bitmap font. This minimal behaviour reveals 30 bytes per second, including while paused. It sets the complete string once so the line does not reflow as letters appear.

local label = -1
local line = "The warehouse is locked."
local elapsed = 0
local visible = 0

function Start(gameObjectId)
label = UI.Find("Dialogue Text")
elapsed, visible = 0, 0
Text.SetText(label, line)
Text.SetVisibleCharacterCount(label, 0)
end

function Update(gameObjectId, deltaTime)
if label < 0 or visible >= #line then return end
elapsed = elapsed + Time.UnscaledDeltaTime()
local nextCount = math.min(#line, math.floor(elapsed * 30))
if Input.GetButtonDown(Input.CROSS) then nextCount = #line end
if nextCount ~= visible then
visible = nextCount
Text.SetVisibleCharacterCount(label, visible)
end
end

For a full dialogue, handle advance/cancel separately and consume the press that completes the text before accepting another press to advance. Animate the panel with Standalone Animations; only disable its Canvas after the exit animation finishes. Play the window-opening sound on the closed-to-open transition, rather than in every Update or per-character step.