Help regarding converting AbsolutePosition to Position(scale)

Quick run-thru:

  1. What do you want to achieve? - A selector that moves to the selected button in a non-hardcoded way(tweening to position of the button and scaling it to the size of the button.)

  2. What is the issue? - All the buttons are sorted by using the UIListLayout, so i cannot rely on the button.Position, since it’s controlled by the said layout.

  3. What solutions have you tried so far? …Looked up a few solutions on Devforum, like this one. Did not work. The second one didn’t work too.

Since i can’t rely on the UDim2 position(influenced by UIListLayout), i somehow need to convert the AbsolutePosition to legit UDim2. That’s my question.

Most tab changing work(the selector behavior too) is stored inside a ModuleScript:

local currenttab --instance to hover back afterwards
local opened
local ts = game:GetService("TweenService")
local ti = TweenInfo.new(0.35,Enum.EasingStyle.Exponential,Enum.EasingDirection.Out)
local selector = game.Players.LocalPlayer.PlayerGui.ScreenGui.MainFrame.Under.Base.Selector
local Main = game.Players.LocalPlayer.PlayerGui.ScreenGui.MainFrame.Under
--tab: string
local function FindMatchingTab(tab)
	local target = tostring(tab)
	local search = Main.Base.Container
	for _,v in pairs(search:GetChildren()) do
		if v:IsA("TextButton") and v.Name == target then
			return v
		end
	end	
end
--DevForum implementation variant. Does not work properly.
local function convertAbsoluteToScale(frame)
	local container = frame.Parent

	local containerAbsPos = container.AbsolutePosition
	local containerAbsSize = container.AbsoluteSize

	local frameAbsPos = frame.AbsolutePosition

	local frameRelativePos = frameAbsPos - containerAbsPos

	return UDim2.fromScale(frameRelativePos.X / containerAbsSize.X, frameRelativePos.Y / containerAbsSize.Y)
end
--


--[[
Opens the UI at the desired tab.

tab: String
]]
function module:OpenAtTab(tab)
	local result = FindMatchingTab(tab)
	if opened == false or opened == nil then
		opened = true
		currenttab = result
		ts:Create(Main,ti,{Size=UDim2.new(0.95, 0,0.895, 0)}):Play()
		ts:Create(selector,ti,{Position = convertAbsoluteToScale(result), Size = result.Size}):Play()
	end
	--TODO: Actual tabs changing. Not yet implemented.
end

If clears things up, this is a snippet. Other logic are just basically placeholders for now…

3 Likes

Is the frame a scrolling frame or just a normal frame? The code required will change slightly depending on it.

The frame is a normal frame, UI Tree looks like this.

Hello! It appears as if you are attempting to do the opposite of what my linked code segment was intending on doing. When you say you want to convert to Position(Scale), I assume that is relative to the parent GUI. I’ve provided a small code segment below that should do just that(grab the Scale position relative to the GUI using absolute position and size data).

local gui = inst:FindFirstAncestorOfClass("ScreenGui")
	local absSize = gui.AbsoluteSize 
	local absPos = gui.AbsolutePosition
	return 
		UDim2.fromScale(inst.AbsolutePosition.X / absSize.X + absPos.X, inst.AbsolutePosition.Y / absSize.Y + absPos.Y),
		UDim2.fromScale(inst.AbsoluteSize.X / absSize.X, inst.AbsoluteSize.Y / absSize.Y)

If you clone/reparent the GuiObject to a ScreenGui or other GuiObject that covers the full screen exactly and set the base GuiObjects position and size to these Scale UDim2 values, it should line up.

2 Likes

hihihi thanks for actually spending time on this question

I tried it and i am genuinely confused what is wrong with this.

I created a mockup UI where everything should line up, based on the

If you clone/reparent the GuiObject to a ScreenGui or other GuiObject that covers the full screen exactly and set the base GuiObjects position and size to these Scale UDim2 values, it should line up.

Everything is scaled.

The results are, well…Underwhelming.

Code, if needed:

--localscript to catch the actual button press. Present here just to show what i pass to the module
local selector = script.Parent.Selector
local container = script.Parent.Frame
local changer = require(game.ReplicatedStorage.Modules.TabFrameworkHandler)

for _,v in pairs(container:GetChildren()) do
	if v:IsA("TextButton") then
		v.MouseButton1Click:Connect(function()
			changer:Change(v)
		end)
	end
end
--module snippet(s)
local viewSize = workspace.Camera.ViewportSize 
function absToScale(inst:GuiObject): (UDim2, UDim2) 
	return 
		UDim2.fromScale(inst.AbsolutePosition.X / viewSize.X, inst.AbsolutePosition.Y / viewSize.Y),
	UDim2.fromScale(inst.AbsoluteSize.X / viewSize.X, inst.AbsoluteSize.Y / viewSize.Y)
end
--
function module:Change(tab:Instance)
	ts:Create(game.Players.LocalPlayer.PlayerGui.test.Frame.Selector,ti,{Position = absToScale(tab), Size = tab.Size}):Play()
	--the selector is currently hardcoded into the tween, will change later
end

I would much rather actually scrap the idea of doing some complicated stuff just to line everything up in a ListLayout.

Marked the last answer as a solution, maybe it will actually help someone… Not in my case, though.

My code segment returns two UDim values. This segment would incorporate both. Also ensure both have an AnchorPoint at 0,0

function module:Change(tab:Instance)
	local position, size = absToScale(tab)
	ts:Create(game.Players.LocalPlayer.PlayerGui.test.Frame.Selector,ti,{Position = position, Size = size}):Play()
end

Let me know how this works, and if it doesn’t please send what the absolute position of both instances are after running the code. Thanks!

1 Like

It works!!(kinda)

The selector’s position is not accurate in any way, but it’s much better than the previous iteration.

Selector AbsPos(when first button is selected): 399.615,241.874
(first)Button AbsPos: 106.8,70.75
изображение

It looks like the AnchorPoints might be messed up. Could you send the file so I can double check that and see if I can figure out what the issue is?

Sure,

devTEST2.rbxl (71.4 KB)

I found the issue, I forgot to account for UI inset by using Camera.ViewSize. I have attached an updated code segment which uses the size and position, and relies off of the Gui instead of the Camera.

function absToScale(inst:GuiObject): (UDim2, UDim2) 
	local gui = inst:FindFirstAncestorOfClass("ScreenGui")
	local absSize = gui.AbsoluteSize 
	local absPos = gui.AbsolutePosition
	return 
		UDim2.fromScale(inst.AbsolutePosition.X / absSize.X + absPos.X, inst.AbsolutePosition.Y / absSize.Y + absPos.Y),
		UDim2.fromScale(inst.AbsoluteSize.X / absSize.X, inst.AbsoluteSize.Y / absSize.Y)
end

The variable gui will reference the parent gui of inst. I’ve also attached a small test place to demonstrate this.


SelectorUIDemo.rbxl (62.1 KB)

1 Like