Scaling your UIs via Code (Obsolete)

EDIT: Roblox has expanded their UI tools a lot as of lately and it pretty much makes this method obsolete although this still does work.

I’ve been working on a new game recently, so I’ve been experimenting with rescaling UIs properly. My first solution worked, but wasn’t as efficient as it could be and required me to keep track of every UI element there is. It was a hassle, so I rethought my solution, and came up with a more mathematical way of doing it. Here is my solution.

This method allows you to make your UIs in studio using offset if you want to, as long as you know the resolution you’re making it with. (Can be seen via ScreenGui’s AbsoluteSize property)

First we define our resolution. (The native resolution you are developing your UI on) You also need to decide if you want the UI to scale based on the X or Y axis. (I usually prefer X axis)

local GUI = script.Parent --ScreenGui
local Updatelog = GUI.UpdateLog
local Resolution = Vector2.new(1397,682)

Now, we will be using this formula from the wiki to calculate the original absolute size and position of the element.

local OrigAbsSize = Vector2.new(Updatelog.Size.X.Offset + Resolution.X * Updatelog.Size.X.Scale,Updatelog.Size.Y.Offset + Resolution.Y * Updatelog.Size.Y.Scale)
local OrigAbsPos = Vector2.new(Updatelog.Parent.AbsolutePosition.X + Updatelog.Position.X.Offset + Updatelog.Parent.AbsoluteSize.X * Updatelog.Position.X.Scale,Updatelog.Parent.AbsolutePosition.Y+Updatelog.Position.Y.Offset + Updatelog.Parent.AbsoluteSize.Y * Updatelog.Position.Y.Scale)

Now we setup a ScreenGui.Changed event to detect if the player changes the resolution of the game. Inside of this event, we will make a ratio of the current GUI AbsoluteSize.X with our original X axis size. After this, we will use the ratio and calculate how many pixels to offset the GUI by.

GUI.Changed:connect(function(Prop)
if Prop == "AbsoluteSize" then
local Ratio = GUI.AbsoluteSize.X/Resolution.X
local CalcSize = Vector2.new(OrigAbsSize.X-(OrigAbsSize.X*Ratio),OrigAbsSize.Y-(OrigAbsSize.Y*Ratio))
local CalcPos = Vector2.new(OrigAbsPos.X-(OrigAbsPos.X*Ratio),OrigAbsPos.Y-(OrigAbsPos.Y*Ratio))
end
end)

Finally for the offset values of the element, we just do the original values - Calculated values

Updatelog.Size = UDim2.new(Updatelog.Size.X.Scale,OrigAbsSize.X-CalcSize.X,Updatelog.Size.Y.Scale,OrigAbsSize.Y-CalcSize.Y)
Updatelog.Position = UDim2.new(Updatelog.Position.X.Scale,OrigAbsPos.X-CalcPos.X,Updatelog.Position.Y.Scale,OrigAbsPos.Y-CalcPos.Y)

Final Code:

local GUI = script.Parent --ScreenGui
local Updatelog = GUI.UpdateLog
local Resolution = Vector2.new(1397,682)
local OrigAbsSize = Vector2.new(Updatelog.Size.X.Offset + Resolution.X * Updatelog.Size.X.Scale,Updatelog.Size.Y.Offset + Resolution.Y * Updatelog.Size.Y.Scale)
local OrigAbsPos = Vector2.new(Updatelog.Parent.AbsolutePosition.X + Updatelog.Position.X.Offset + Updatelog.Parent.AbsoluteSize.X * Updatelog.Position.X.Scale,Updatelog.Parent.AbsolutePosition.Y+Updatelog.Position.Y.Offset + Updatelog.Parent.AbsoluteSize.Y * Updatelog.Position.Y.Scale)

GUI.Changed:connect(function(Prop)
if Prop == "AbsoluteSize" then
local Ratio = GUI.AbsoluteSize.X/Resolution.X
local CalcSize = Vector2.new(OrigAbsSize.X-(OrigAbsSize.X*Ratio),OrigAbsSize.Y-(OrigAbsSize.Y*Ratio))
local CalcPos = Vector2.new(OrigAbsPos.X-(OrigAbsPos.X*Ratio),OrigAbsPos.Y-(OrigAbsPos.Y*Ratio))
Updatelog.Size = UDim2.new(Updatelog.Size.X.Scale,OrigAbsSize.X-CalcSize.X,Updatelog.Size.Y.Scale,OrigAbsSize.Y-CalcSize.Y)
Updatelog.Position = UDim2.new(Updatelog.Position.X.Scale,OrigAbsPos.X-CalcPos.X,Updatelog.Position.Y.Scale,OrigAbsPos.Y-CalcPos.Y)
end
end)

Video of it working:(Only Updatelog in the video I setup to scale, Also scales down to Mobile fine)
https://dl.dropboxusercontent.com/u/53742709/ShareX/2016/05/2016-05-24_17-35-05.mp4

I know I didn’t explain the math very much, but you should be able to deduce what I’m doing. I’m working on a module using this method, but I figured I’d release my method so you can do it yourself before I do. (You also might want to turn it into a function so you can call it when the game starts up as the changed event won’t fire when a player joins if they have a different screen res)

7 Likes

I used the absolute screen size x and y to make the crosshair in my game accurately reflect the 'real world 'size" and it worked quite well.

This looks interesting as well, couldn’t you have used SizeConstraints, though?

Oh, yeah. Definitely. My last method was a combination of XX size constraint and AbsoluteSize/Position, but my method resulted in me having to set the values in code and constantly recalculate them. It made for a messy code base of keeping track of so many positions.

This more or less lets you be able to make your UI in any fashion you want in Studio and have it translates across screen sizes just fine.

There are multiple ways to handle UI scaling, this is just one of them.

2 Likes

I use something like this in a game I’m working on, but my ratio is
(GUI.AbsoluteSize.X + GUI.AbsoluteSize.Y) / (Resolution.X + Resolution.Y)

rather than just basing the scale on the X size

1 Like

Hey @IntellectualBeing this may come in handy

It might scale to mobile properly, but the actual size of the GUI would be way too small to use/see on phones.


Not…Really?

This is mobile with my previous method and it even scales pretty good. Its not designed for mobile, but I wouldn’t say the scaling to be way too small to use/see on phones. (The new method does deal with text better)

Yeah I thought about this. However, everyone has a different resolution. By looking at the code it looks like you can only set it to scale to only one resolution. I haven’t coded in a while so I could be wrong.

Just change local Resolution from Vector2.new(x,x) to Camera.ViewportSize to get the resolution

Nevermind then

1 Like

@Usering

No no no. The resolution variable is not setting it to scale to one resolution. The variable is the native resolution you are developing your UI on in Studio. It uses that variable to scale to all other resolutions via a ratio of how much smaller or bigger the player’s resolution is compared to the native one.

2 Likes

This can result in awkward scaling if your UIs aren’t designed for this method in particular, but hey if it works go for it.

I’ve always found that this doesn’t quite work nicely for everything. When I design mine I design 3 version. Small, medium and large. I then look at the resolution of the screen to decide which one to show. It means I have a lot more control over it but it does require a little more work. Worth it to look good on every screen.

modern games do this but with resolution instead

if I had more time, I would design UI for the most common resolutions: 16:9 and 4:3. (source: Screen resolution statistics) Only thing that isn’t foolproof is that people can change the size of Roblox’s windows themselves, which could result in unexpected visuals.