Incorrect value returned by TextService:GetTextBoundsAsync() (or possible incorrect use ?)

Hello everyone here, I really need help.
I have rewrote a chat script from a game in order to make it able to wrap lines.
The problem is that the result is different depending on the platform.

  1. What do I want to achieve: I am trying to make the text display correctly. The message itself is composed of two TextLabels, the first one is containing the username and the rank, and the second one contains the content of the message:
    image
    Well this seem to work perfectly ! It even works on mutiple lines:


    And this leads us to the second point,

  2. But, where is the issue: Well, randomly discovered what caused this, but it appears that the chat is not working properly on some devices. What happens is that the content of the message is cut (letters or even words) at the end. This issue happens when you set the display zoom to 125 % in Windows settings. With a zoom of 100 %, no issues seem to happen. Here is what happens with a zoom of 125 %:

  3. What have I tried to fix this: I have tried to do some changes to ensure accuracy in the code, but I am sure that it is not related and that the real problem is located in the Roblox Engine.

Some technical details: the resolution of my monitor is 1920x1080.
Under the hood I have seen that the number of pixels returned by TextService:GetTextBoundsAsync() goes from an integer (with 100% zoom) to a lower float number (with 125% zoom)

Here are some screenshot to compare sizes between Windows zooms:
100% zoom:
image
125% zoom:


(even the ELO doesn’t display)

Here is a chunk of the faulty code (contains debug prints, that you can match with the screenshots, and yes, I know this is probably not the best code but I’m sticking with this coding technique):

local function showMessage(from : Player, message : string, isTeam : boolean, isDead : boolean, customColor : Color3, plrRank : string, plrColor : BrickColor)
	local clonedTemplate = chatElementTemplate:Clone()
	local clonedTemplateText = clonedTemplate:WaitForChild("Txt")
	
	print("DEBUG: DispData")
	print(from)
	print(message)
	print(isTeam)
	print(isDead)
	print(customColor)
	print(plrRank)
	print(plrColor)

	local textBoundsParams = Instance.new("GetTextBoundsParams")
	textBoundsParams.Font = clonedTemplateText.FontFace
	textBoundsParams.Size = clonedTemplateText.TextSize
	textBoundsParams.Width = 0


	local textBoundsParams2 = Instance.new("GetTextBoundsParams")
	textBoundsParams2.Font = clonedTemplate.FontFace
	textBoundsParams2.Size = clonedTemplate.TextSize
	textBoundsParams2.Width = 0
	
	clonedTemplate.Name = "ChatText"
	
	if plrRank == nil then
		plrRank = "norank"
	end
	
	clonedTemplateText.Text = message
	if from ~= nil then
		
		local realName = ""
		
		if isTeam then
			realName ..= translatorLib.GetTranslationOfKey("ChatUtils.Team") .. " "
		end
		
		if plrRank ~= "norank" then
			realName ..= translatorLib.GetTranslationOfKey("Roles.String_" .. tostring(plrRank)) .. " "
		end
		
		if isDead then
			realName ..= translatorLib.GetTranslationOfKey("ChatUtils.Dead") .. " "
		else
			if plrColor ~= nil then
				clonedTemplate.TextColor3 = plrColor.Color
			end
		end
		
		realName ..= from.Name
		
		clonedTemplate.Text = realName .. ":"
	end
	
	textBoundsParams2.Text = clonedTemplate.Text
	
	if customColor ~= nil then
		clonedTemplateText.TextColor3 = customColor
	end
	
	local positionOffset = 0
	
	if from ~= nil then
		positionOffset = textService:GetTextBoundsAsync(textBoundsParams2).X + 5
		print("Computed positional offset 1")
		print(positionOffset)
	end
	clonedTemplateText.Position = UDim2.new(0, positionOffset, 0, 0)

	textBoundsParams.Text = message
	textBoundsParams.Width = (chatScroll.AbsoluteCanvasSize.X - 12) - positionOffset
	local computedSize = textService:GetTextBoundsAsync(textBoundsParams)
	print("Computed positional offset 2")
	print(computedSize)
	clonedTemplateText.Size = UDim2.new(0, computedSize.X, 0, computedSize.Y)
	
	clonedTemplate.Size = UDim2.new(clonedTemplate.Size.X.Scale, clonedTemplate.Size.X.Offset, clonedTemplate.Size.Y.Scale, computedSize.Y)
	
	clonedTemplate.Parent = chatScroll
	
	textBoundsParams:Destroy()
	textBoundsParams2:Destroy()
	print("==================================================")
end

Any help would be greatly appreciated :grinning:, because it’s kind of frustrating to see only 9/10 of your message in chat.

Anyways, good evening !

This has been a problem that i’ve had for a little while now. If anyone can help OP, it would also help me greatly

1 Like

Putting the fault on Roblox Engine prevented me to search for a workaround.
I tried to find one and I did:

If you use math.ceil() on the two coordinates returned by TextService:GetTextBoundsAsync(), you will be getting correct coordinates that can be used for setting the Offset of UDim2 of some UI TextLabel.

For me this solution is still a workaround, so I’m not really thinking that it is a good idea to mark this answer as a solution. Roblox should precise that it may return decimal values or instead they should do the math.ceil inside of the code of TextService:GetTextBoundsAsync(), because half of a pixel doesn’t exists.

The fixed code looks like that:

local function showMessage(from : Player, message : string, isTeam : boolean, isDead : boolean, customColor : Color3, plrRank : string, plrColor : BrickColor)
	local clonedTemplate = chatElementTemplate:Clone()
	local clonedTemplateText = clonedTemplate:WaitForChild("Txt")

	local textBoundsParams = Instance.new("GetTextBoundsParams")
	textBoundsParams.Font = clonedTemplateText.FontFace
	textBoundsParams.Size = clonedTemplateText.TextSize
	textBoundsParams.Width = 0


	local textBoundsParams2 = Instance.new("GetTextBoundsParams")
	textBoundsParams2.Font = clonedTemplate.FontFace
	textBoundsParams2.Size = clonedTemplate.TextSize
	textBoundsParams2.Width = 0
	
	clonedTemplate.Name = "ChatText"
	
	if plrRank == nil then
		plrRank = "norank"
	end
	
	clonedTemplateText.Text = message
	if from ~= nil then
		
		local realName = ""
		
		if isTeam then
			realName ..= translatorLib.GetTranslationOfKey("ChatUtils.Team") .. " "
		end
		
		if plrRank ~= "norank" then
			realName ..= translatorLib.GetTranslationOfKey("Roles.String_" .. tostring(plrRank)) .. " "
		end
		
		if isDead then
			realName ..= translatorLib.GetTranslationOfKey("ChatUtils.Dead") .. " "
		else
			if plrColor ~= nil then
				clonedTemplate.TextColor3 = plrColor.Color
			end
		end
		
		realName ..= from.Name
		
		clonedTemplate.Text = realName .. ":"
	end
	
	textBoundsParams2.Text = clonedTemplate.Text
	
	if customColor ~= nil then
		clonedTemplateText.TextColor3 = customColor
	end
	
	local positionOffset = 0
	
	if from ~= nil then
		positionOffset = math.ceil(textService:GetTextBoundsAsync(textBoundsParams2).X) + 5
	end
	clonedTemplateText.Position = UDim2.new(0, positionOffset, 0, 0)

	textBoundsParams.Text = message
	textBoundsParams.Width = (chatScroll.AbsoluteCanvasSize.X - 12) - positionOffset
	local computedSize = textService:GetTextBoundsAsync(textBoundsParams)
	
	computedSize = Vector2.new(math.ceil(computedSize.X), math.ceil(computedSize.Y)) -- https://devforum.roblox.com/t/incorrect-value-returned-by-textservicegettextboundsasync-or-possible-incorrect-use/2544580
	
	clonedTemplateText.Size = UDim2.new(0, computedSize.X, 0, computedSize.Y)
	
	clonedTemplate.Size = UDim2.new(clonedTemplate.Size.X.Scale, clonedTemplate.Size.X.Offset, clonedTemplate.Size.Y.Scale, computedSize.Y)
	
	clonedTemplate.Parent = chatScroll
	
	textBoundsParams:Destroy()
	textBoundsParams2:Destroy()
end

Have a nice day.

1 Like