Skip to content

Displaying and Updating Text

The Text widget renders a string using a signed-distance-field font. Use it for scores, labels, dialogue, timers, and any other on-screen text.


Creating a Text Widget

C++

#include "Nodes/Widgets/Text.h"

Text* label = parentWidget->CreateChild<Text>("ScoreLabel");
label->SetText("Score: 0");
label->SetTextSize(24.0f);

Lua

local label = self:CreateChild("Text")
label:SetName("ScoreLabel")
label:SetText("Score: 0")
label:SetTextSize(24.0)

Editor: Add a Text node in the scene hierarchy. Set the text string, font, and size in the Properties panel.


Setting Font

Assign a font asset. If no font is set, the engine uses its default font.

// C++
label->SetFont(LoadAsset<Font>("F_MainFont"));

// Lua
label:SetFont(LoadAsset("F_MainFont"))

Text Size

Text size is in pixels:

label:SetTextSize(32.0)
local size = label:GetTextSize()
local scaledSize = label:GetScaledTextSize() -- accounts for widget scale

Color

Text color is set through the widget's SetColor():

// C++
label->SetColor({1.0f, 1.0f, 0.0f, 1.0f}); // yellow

// Lua
label:SetColor(Vec(1, 1, 0, 1)) -- yellow

Updating Text Dynamically

Change the displayed string at any time with SetText(). This is how you update scores, timers, and other dynamic values.

C++

void ScoreHUD::Tick(float deltaTime)
{
    Widget::Tick(deltaTime);
    std::string scoreStr = "Score: " + std::to_string(mScore);
    mScoreText->SetText(scoreStr);
}

Lua

function ScoreHUD:Tick(deltaTime)
    self.scoreText:SetText("Score: " .. tostring(self.score))
end

Timer Example

function TimerUI:Start()
    self.timeRemaining = 60.0
    self.timerText = self:CreateChild("Text")
    self.timerText:SetAnchorMode(AnchorMode.TopMid)
    self.timerText:SetPosition(0.0, 10.0)
    self.timerText:SetTextSize(28.0)
    self.timerText:SetHorizontalJustification(Justification.Center)
end

function TimerUI:Tick(deltaTime)
    self.timeRemaining = self.timeRemaining - deltaTime
    if self.timeRemaining < 0.0 then
        self.timeRemaining = 0.0
    end

    local minutes = math.floor(self.timeRemaining / 60)
    local seconds = math.floor(self.timeRemaining % 60)
    self.timerText:SetText(string.format("%d:%02d", minutes, seconds))
end

Justification

Text supports horizontal and vertical alignment within the widget's bounds.

Horizontal Justification

Value Behavior
Justification.Left Align text to the left edge (default)
Justification.Center Center text horizontally
Justification.Right Align text to the right edge

Vertical Justification

Value Behavior
Justification.Top Align text to the top (default)
Justification.Center Center text vertically
Justification.Bottom Align text to the bottom
// C++
label->SetHorizontalJustification(Justification::Center);
label->SetVerticalJustification(Justification::Center);

// Lua
label:SetHorizontalJustification(Justification.Center)
label:SetVerticalJustification(Justification.Center)

Word Wrapping

Enable word wrap to automatically break text at the widget's width boundary:

local description = self:CreateChild("Text")
description:SetAnchorMode(AnchorMode.FullStretch)
description:SetRatios(0.1, 0.3, 0.8, 0.4)
description:SetTextSize(18.0)
description:EnableWordWrap(true)
description:SetText("This is a long description that will wrap to fit within the widget bounds.")

Outline

Text supports an outline effect for readability against busy backgrounds:

// C++
label->SetOutlineColor({0.0f, 0.0f, 0.0f, 1.0f}); // black outline

// Lua
label:SetOutlineColor(Vec(0, 0, 0, 1))

The outline size, softness, and cutoff are configurable in the Properties panel in the editor. You can read these values at runtime:

local outlineSize = label:GetOutlineSize()
local softness = label:GetSoftness()
local cutoff = label:GetCutoff()

Measuring Text Dimensions

Get the rendered width and height of the current text string. This is useful for dynamically sizing containers or positioning adjacent elements.

local width = label:GetTextWidth()
local height = label:GetTextHeight()

Scaled extents account for the widget's scale:

local minExtent = label:GetScaledMinExtent()
local maxExtent = label:GetScaledMaxExtent()

Example: Score Counter

C++

void GameHUD::Create()
{
    Canvas::Create();
    SetAnchorMode(AnchorMode::FullStretch);
    SetRatios(0.0f, 0.0f, 1.0f, 1.0f);

    mScoreText = CreateChild<Text>("Score");
    mScoreText->SetAnchorMode(AnchorMode::TopRight);
    mScoreText->SetPosition(-150.0f, 10.0f);
    mScoreText->SetDimensions(140.0f, 40.0f);
    mScoreText->SetTextSize(24.0f);
    mScoreText->SetHorizontalJustification(Justification::Right);
    mScoreText->SetColor({1.0f, 1.0f, 1.0f, 1.0f});
    mScoreText->SetText("Score: 0");
}

void GameHUD::UpdateScore(int score)
{
    mScoreText->SetText("Score: " + std::to_string(score));
}

Lua

function GameHUD:Start()
    self:SetAnchorMode(AnchorMode.FullStretch)
    self:SetRatios(0.0, 0.0, 1.0, 1.0)

    self.scoreText = self:CreateChild("Text")
    self.scoreText:SetAnchorMode(AnchorMode.TopRight)
    self.scoreText:SetPosition(-150.0, 10.0)
    self.scoreText:SetDimensions(140.0, 40.0)
    self.scoreText:SetTextSize(24.0)
    self.scoreText:SetHorizontalJustification(Justification.Right)
    self.scoreText:SetColor(Vec(1, 1, 1, 1))
    self.scoreText:SetText("Score: 0")
end

function GameHUD:UpdateScore(score)
    self.scoreText:SetText("Score: " .. tostring(score))
end

Further Reading