I want to animate characters individually, but that’s not possible without creating a textlabel for every character in a string of text.
There’s this reply to a post that has a collection of solutions for that, and this particular one does a good job. However, the text can look ugly when you aren’t using monospaced font:
Here’s sample code that uses it, assuming it is a LocalScript and it is parented to a ScreenGui with a Frame:
Sample Code
local TextService = game:GetService("TextService")
local TextBoxHandler = {}
TextBoxHandler.Root = script.Parent.Frame
TextBoxHandler.Speaker = {
Font = Font.fromEnum(Enum.Font.RobotoMono),
FontSize = 16,
TextColor = Color3.new(0, 0, 0)
}
TextBoxHandler._currentPos = Vector2.new()
TextBoxHandler._defaultPos = Vector2.new()
TextBoxHandler._currentTextPos = 0
TextBoxHandler._textLabels = {}
function TextBoxHandler:AddText(text: string, effects: {TextEffect}?, ...: TextArguments)
local textArguments = {...}
local params = Instance.new("GetTextBoundsParams")
-- params.Text = text
params.Font = self.Speaker.Font
params.Size = self.Speaker.FontSize
params.Width = self.Root.AbsoluteSize.X
local startPoint = self._currentTextPos
local labelList = {}
for word in text:gmatch("%S+") do
word = word .. " "
params.Text = word
local textSize = TextService:GetTextBoundsAsync(params)
if self._currentPos.X + textSize.X > self.Root.AbsoluteSize.X then
self._currentPos = Vector2.new(self._defaultPos.X, self._currentPos.Y + self.Speaker.FontSize)
end
for i = 1, #word do
self._currentTextPos += 1
local c = word:sub(i, i)
params.Text = c
local textSize = TextService:GetTextBoundsAsync(params)
local charLabel = Instance.new("TextLabel")
charLabel.Size = UDim2.fromOffset(textSize.X, textSize.Y)
charLabel.Position = UDim2.fromOffset(self._currentPos.X, self._currentPos.Y)
charLabel.Text = c
charLabel.Transparency = 1
charLabel.FontFace = self.Speaker.Font
charLabel.TextSize = self.Speaker.FontSize
charLabel.TextColor3 = self.Speaker.TextColor
charLabel.RichText = true
charLabel.Name = tostring(self._currentTextPos)
charLabel.Parent = (self.Root :: Frame) :: Instance
table.insert(labelList, charLabel)
self._textLabels[self._currentTextPos] = charLabel
self._currentPos += Vector2.new(textSize.X, 0)
end
end
self._textLabels[#self._textLabels]:Destroy()
if effects then
for i, effect in effects do
effect(labelList, startPoint, table.unpack(textArguments[i] or {}))
end
end
local charDelays = self.Speaker.CharDelays or {} :: {[string]: number}
for _, label in labelList do
label.Transparency = 0
label.BackgroundTransparency = 1
local charDelay = charDelays[label.Text]
if charDelay then
task.wait(charDelay)
else
task.wait(self.Speaker.SpeakingDelay)
end
end
-- effect(labelList, startPoint, ...)
end
TextBoxHandler:AddText("This is a test phrase.", {
})
Monospaced Font: RobotoMono
It looks correctly spaced because it is monospaced.
Nonmonospaced Font: Gotham
This is close, but not right. You can see some characters have an uneven amount of spacing.
These results are done by just changing the font in Line 6.
So, is there any solution that can space these non-monospaced characters correctly?