How would I "preview" a font size?

I’m making a dialogue box, and I want it so that the text obviously fits on the screen, but furthermore, I want there to be a “typewriter” effect when displaying the text.

However, I don’t want the font to change size as more text gets added (as would happen with TextScaled or anything like that) I want it so that the font size remains constant as it displays the message. And the font size will change based on the length of the message.

I’m aware of things like TextService:GetTextSize() which allow me to attain the Vector2 bounds, but how would I efficiently conclude the font size to use so that the text displays within its bounds?

Any help with figuring this out is appreciated.

1 Like

After experimenting with TextService:GetTextSize() for a while, I figured out how to use that to cycle through various TextSize values until it fit within the AbsoluteSize of the TextLabel at a given size.

However, I later realized that there’s a TextFits property, which is automatically set to true when all of the text fits within the bounds of the TextLabel. This simplifies things a bit, as you wouldn’t need to use TextService:GetTextSize() at all.


Because there are good use cases for both (with the first option making it possible for you to determine its TextBounds, which provides you with the opportunity to increase the Size of the TextLabel to accommodate all the text rather than decreasing the TextSize), I’ll include both variations in this post.


The first completed codeblock will be the “simpler” version that better matches the use case you described and the second codeblock will be the one that uses TextService:GetTextSize() if you want to really really make sure the text fits and / or to have more flexibility.


Since you didn’t include the code for the typewriter effect in your original post, I utilized the example code snippet from the Roblox Creator Documentation site and integrated that into the completed product to test your use case.

Before I explain how it works, here’s the full script:

(Simple Version: Only checking for TextFits) Completed LocalScript code

local TweenService = game:GetService("TweenService")

local textLabel = script.Parent

local maximumTextSize = 100 -- Update this to the largest possible value you would want the "TextSize" property to be
local testingText = {
	
	[1] = "Hello world!",
	[2] = "Here is an example string with a lot more text than the previous one.",
	[3] = "Isn't that cool?"
	
}

local typewriterTweenInfo = TweenInfo.new(4, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local previewTextSizeChanges = false -- If you want to visualize the Text as the script is trying to find out a suitable TextSize, change this to true (mostly for testing purposes)

local function typewriter(originalText)
	
	if previewTextSizeChanges == true then -- Mostly for testing purposes!
		textLabel.MaxVisibleGraphemes = -1
	else
		textLabel.MaxVisibleGraphemes = 0
	end
	
	textLabel.Text = originalText
	
	
	
	local currentTextSize = maximumTextSize
	
	local finalTextSize
	while finalTextSize == nil do
		task.wait()
		
		textLabel.TextSize = currentTextSize
		if textLabel.TextFits == true then
			finalTextSize = currentTextSize
		else
			currentTextSize -= 1
		end
	end
	
	textLabel.TextSize = finalTextSize
	
	if previewTextSizeChanges == true then -- Mostly for testing purposes!
		task.wait(2) -- Small delay before starting the typewriter effect to visualize how the final text will appear at what was determined to be a suitable TextSize
	end
	
	local tween = TweenService:Create(textLabel, typewriterTweenInfo, {
		MaxVisibleGraphemes = utf8.len(textLabel.ContentText),
	})
	
	tween:Play()
	tween.Completed:Wait()
end

for _, textToAnimate in testingText do
	typewriter(textToAnimate)
end


(Alternative Version: Using TextService:GetTextSize())

Completed LocalScript code

local TweenService = game:GetService("TweenService")
local TextService = game:GetService("TextService")

local textLabel = script.Parent

local maximumTextSize = 100 -- Update this to the largest possible value you would want the "TextSize" property to be
local testingText = {
	
	[1] = "Hello world!",
	[2] = "Here is an example string with a lot more text than the previous one.",
	[3] = "Isn't that cool?"
	
}

local typewriterTweenInfo = TweenInfo.new(4, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local previewTextSizeChanges = false -- If you want to visualize the Text as the script is trying to find out a suitable TextSize, change this to true (mostly for testing purposes)

local function typewriter(originalText)
	
	if previewTextSizeChanges == true then -- Mostly for testing purposes!
		textLabel.MaxVisibleGraphemes = -1
	else
		textLabel.MaxVisibleGraphemes = 0
	end
	
	textLabel.Text = originalText
	
	
	
	local currentTextSize = maximumTextSize
	local font = textLabel.Font
	local absoluteSize = textLabel.AbsoluteSize
	
	
	local finalTextSize
	while finalTextSize == nil do
		task.wait()
		
		textLabel.TextSize = currentTextSize
		local requiredSpace = TextService:GetTextSize(originalText, currentTextSize, font, absoluteSize)
		
		if 
			requiredSpace.X <= absoluteSize.X
			and requiredSpace.Y <= absoluteSize.Y
			and textLabel.TextFits == true
		then
			finalTextSize = currentTextSize
		else
			currentTextSize -= 1
			--print(requiredSpace.X, requiredSpace.Y, absoluteSize.X, absoluteSize.Y)
			--print(currentTextSize)
		end
	end
	
	textLabel.TextSize = finalTextSize
	
	if previewTextSizeChanges == true then -- Mostly for testing purposes!
		task.wait(2) -- Small delay before starting the typewriter effect to visualize how the final text will appear at what was determined to be a suitable TextSize
	end
	
	local tween = TweenService:Create(textLabel, typewriterTweenInfo, {
		MaxVisibleGraphemes = utf8.len(textLabel.ContentText),
	})
	
	tween:Play()
	tween.Completed:Wait()
end

for _, textToAnimate in testingText do
	typewriter(textToAnimate)
end

Instructions

  1. If the script that contains the code is not placed directly into the TextLabel that you want to preview text for, make sure to update the local textLabel variable to reference where the TextLabel is in the game.

  2. Update the “maximumTextSize” to the largest possible value you would want the “TextSize” property to be. It’s at 100 by default since that’s the maximum value you can input via the Properties window.

  3. Optionally, update the local previewTextSizeChanges variable from false to true (right above local function typewriter) if you want to visualize the Text as the script is trying to find out a suitable TextSize (mostly for testing purposes). For the most noticeable effect, make the TextLabel smaller so that the TextSize will have to be decreased to an even lower value.

Also, because this is an example, the function that handles everything runs immediately after beginning a playtest. As a result, additional changes would be necessary to properly integrate this into your game.


How the “preview” works

for _, textToAnimate in testingText do
	typewriter(textToAnimate)
end

The text that you want to find the optimal size for is sent through to the typewriter function (and in this case, it’s everything included within the testingText table).


if previewTextSizeChanges == true then -- Mostly for testing purposes!
	textLabel.MaxVisibleGraphemes = -1
else
	textLabel.MaxVisibleGraphemes = 0
end

textLabel.Text = originalText

Since we’re creating a typewriter effect, the text is hidden by setting TextLabel.MaxVisibleGraphemes to 0. After that, the Text property of the TextLabel is updated to the string that was sent through the function.


local currentFontSize = maximumTextSize
local font = textLabel.Font
local absoluteSize = textLabel.AbsoluteSize

These 3 variables have the following purposes:

  • currentFontSize initially refers to the value of maximumTextSize at the top of the script. This is the first value that is used when checking if the text fits in the TextLabel. If it doesn’t, the value is continuously lowered and the TextSize is updated to the new value until it’s able to fit in the TextLabel.

(The last 2 variables are only included in the second version of the script because they are necessary for TextService:GetTextSize())

  • font refers to the TextLabel.Font that is currently being used to make sure the script will be accurately checking how much space will be necessary . If you intend to swap the font at different points in time, I’d recommend doing so before it reaches this point in the function (around the time that textLabel.Text = originalText is assigned). However, if you’re using the simpler version of the script, this wouldn’t be necessary.

  • absoluteSize refers to the AbsoluteSize of the TextLabel, which basically says what the resolution of the TextLabel is on the current screen. This will differ for the same object depending on the size of the screen.


local finalTextSize
while finalTextSize == nil do

The finalTextSize variable is where the value of the new TextSize will be stored once the script figures out the largest possible TextSize where all of the text is visible. We create a loop that continues as long as finalTextSize == nil (meaning that there’s no value for it yet).


-- This is what the loop looks like in the simpler version
	while finalTextSize == nil do
		task.wait()
		
		textLabel.TextSize = currentTextSize
		if textLabel.TextFits == true then
			finalTextSize = currentTextSize
		else
			currentTextSize -= 1
		end
	end

I’ll start by explaining the loop in the simpler version, as it’ll make it easier to explain the second one afterward.

We start by adding a task.wait() to make sure the game doesn’t freeze from the loop running too quickly. Then, we update the TextSize of the TextLabel to the value stored in the currentTextSize variable outside of the loop.

Next, we check if the TextFits property of the TextLabel is equal to true. If it doesn’t, that means the text doesn’t fit within the TextLabel, which means we move on to the else statement that decreases the currentTextSize variable by 1. If that happens, the loop will restart and try again.

If TextFits ends up equalling true, we know that all of the Text is visible at its current TextSize. From there, we update the finalTextSize variable to equal the currentTextSize, which will end the loop on the next iteration and continue with the script.


-- This is what the loop looks like in the alternative version
	while finalTextSize == nil do
		task.wait()
		
		textLabel.TextSize = currentTextSize
		local requiredSpace = TextService:GetTextSize(originalText, currentTextSize, font, absoluteSize)
		
		if 
			requiredSpace.X <= absoluteSize.X
			and requiredSpace.Y <= absoluteSize.Y
			and textLabel.TextFits == true
		then
			finalTextSize = currentTextSize
		else
			currentTextSize -= 1
			--print(requiredSpace.X, requiredSpace.Y, absoluteSize.X, absoluteSize.Y)
			--print(currentTextSize)
		end
	end

The alternative version is almost identical to the previous one, except that there’s additional values that are checked for. In this case, TextService:GetTextSize() is being used to check how much space is required for all of the Text to fit within the TextLabel. We can use that value to compare it to the current AbsoluteSize of the TextLabel; if it’s less than that, then we know the Text probably fits within the TextLabel. However, in some cases, it still did not fit within the TextLabel, which is when I found out about the TextFits property and added that in.

Ultimately, checking for all 3 of these things is not really necessary (since you can just reference TextFits), but you can do that if you want to be super sure that all of the Text will be visible. And as mentioned at the beginning of this post, using TextService:GetTextSize() does provide the opportunity to increase the Size of the TextLabel until it reaches an AbsoluteSize that would accommodate the text, which may be useful if you want to ensure the text remains readable without decreasing the TextSize.


textLabel.TextSize = finalTextSize

After the loop finishes, we can update the TextSize of the TextLabel to the finalTextSize, which should be the largest value it can be while making sure all of the text is visible.


	if previewTextSizeChanges == true then -- Mostly for testing purposes!
		task.wait(2) -- Small delay before starting the typewriter effect to visualize how the final text will appear at what was determined to be a suitable TextSize
	end
	
	local tween = TweenService:Create(textLabel, typewriterTweenInfo, {
		MaxVisibleGraphemes = utf8.len(textLabel.ContentText),
	})
	
	tween:Play()
	tween.Completed:Wait()
end

The code at the very end of the function handles the typewriter effect with the newly configured TextLabel (and as mentioned earlier, the original typewriter example can be found in a code snippet from the Roblox Creator Documentation site.


And I think that’s everything! Hopefully this was useful and the explanations made sense :smile: I’ve been working on this for several hours past midnight, so if I completely forgot to explain something or you have any questions, feel free to ask and I’ll try to respond once I am fully rested.

2 Likes

Wow!! Thanks for including the explanation along with a working script. It works pretty much perfectly (although I will change a couple things but that was expected, wasn’t it?)

I’ll put a video in case anyone stumbles on this and wants a visualization.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.