Skip to content
Berke edited this page Oct 10, 2026 · 1 revision

14 - UI

UI elements are Entities with UI components:

Component Accessor Members
UIText entity.uiText text
UISprite entity.uiSprite textureIndex
UITransform entity.uiTransform position, scale, rotation, anchorMin, anchorMax, pivot, and read-only resolvedPosition, resolvedSize

Each accessor is nil when the Entity doesn't have that component, so check before using it.


Health readout

Attach to: a UI Entity with a UIText.

Shows the player's health as text, using the Health script from 10_health_and_damage.md.

-- Scripts/UI/HealthHud.lua (entity script)
public Entity player = nil

local label
local health
local shown = ""

function Start()
    label = entity.uiText

    if label == nil then
        Debug.LogError("HealthHud: " .. entity.name .. " has no UIText")
        return
    end

    if player ~= nil then health = player:GetScript("Health") end
end

function Update()
    if label == nil or health == nil or not health.isValid then return end

    local text = "HP " .. math.ceil(health.currentHealth) .. " / " .. math.ceil(health.maxHealth)

    if text ~= shown then
        shown = text
        label.text = text
    end
end

Notes

  • math.ceil avoids showing a fractional health like 99.4 while regenerating, and it means the player never sees HP 0 while still alive.

Popup messages

Attach to: a UI Entity with a UIText. One per HUD is enough.

A message that appears for a few seconds and then clears. Other scripts trigger it by calling Show.

-- Scripts/UI/MessageBox.lua (entity script)
local label
local timer = 0.0

function Start()
    label = entity.uiText

    if label == nil then
        Debug.LogError("MessageBox: " .. entity.name .. " has no UIText")
        return
    end

    label.text = ""
end

-- Public: other scripts call messageBox:Show("text", seconds)
function Show(self, message, seconds)
    if label == nil then return end

    label.text = message
    timer = seconds or 2.0
end

function Update()
    if timer <= 0 then return end

    timer = timer - GameTime.deltaTime
    if timer <= 0 and label ~= nil then label.text = "" end
end

Calling it from another script (for example, the locked door in 05_doors.md):

public Behaviour messageBox = nil

-- ...then, where the door used to call Debug.Print:
if messageBox ~= nil then
    messageBox:Show("This door needs the red key", 2.5)
else
    Debug.Print("This door needs the red key")
end

Notes

  • Assign messageBox to the script on the message Entity, not to the Entity.
  • seconds or 2.0 makes the duration optional: leave it off and you get two seconds.

Pulsing icon

Attach to: a UI Entity with a UITransform, such as a "low health" warning.

Breathes the element's scale in and out around whatever scale it was given in the editor.

-- Scripts/UI/PulseIcon.lua (entity script)
public number pulseAmount = 0.2
public number pulsesPerSecond = 1.5

local ui
local baseScale
local clock = 0.0

function Start()
    ui = entity.uiTransform

    if ui == nil then
        Debug.LogError("PulseIcon: " .. entity.name .. " has no UITransform")
        return
    end

    baseScale = ui.scale
end

function Update()
    if ui == nil then return end

    clock = clock + GameTime.deltaTime * pulsesPerSecond * 2 * math.pi

    -- Multiply the *original* scale, so the pulse never drifts.
    local k = 1 + math.sin(clock) * pulseAmount
    ui.scale = Vector2(baseScale.x * k, baseScale.y * k)
end

Notes

  • Capturing baseScale once in Start is the important part. Multiplying the current scale every frame would make the icon grow or shrink without bound.

Clone this wiki locally