Top-Down Map 2D to 3D Position

I am trying to make something where an ImageButton is a top-down view of the in-game world, and it’s working as intended, at least accurately along the in-game X-Axis. Though the in-game Z-Axis seems slightly offset, I do not know why.

To be specific, the top-down map is for the player to click on and retrieve a Vector3 position relative to where the player clicked on the ImageButton.

local localPlayer = game.Players.LocalPlayer

local imageButton = script.Parent

local worldWidth = 2048
local worldHeight = 2048

--// 400x400
local imageButtonWidth = imageButton.AbsoluteSize.X
local imageButtonHeight = imageButton.AbsoluteSize.Y

local scaleX = worldWidth / imageButtonWidth
local scaleZ = worldHeight / imageButtonHeight

imageButton.MouseButton1Down:Connect(function(x, y)
	local pixelPosition = Vector2.new(x - imageButton.AbsolutePosition.X - imageButtonWidth * 0.5, y - imageButton.AbsolutePosition.Y - imageButtonHeight * 0.5)

	local xPos = math.floor(pixelPosition.X * scaleX + 0.5)
	local zPos = math.floor(pixelPosition.Y * scaleZ + 0.5)

	print(pixelPosition)
	print(xPos, zPos)

	localPlayer.Character.HumanoidRootPart.CFrame = CFrame.new(Vector3.new(xPos, 15, zPos))
end)

1 Like

This math doesn’t look right:

	local xPos = math.floor(pixelPosition.X * scaleX + 0.5)
	local zPos = math.floor(pixelPosition.Y * scaleZ + 0.5)

if pixelPosition is a positive number of pixels up to imageButtonHeight/Width, then multiplying it by scale should give you between 0.0 and worldHeight/Width. Adding 0.5 to that doesn’t seem right. I think it should be:

local xPos = math.floor((pixelPosition.X / imageButtonWidth - 0.5) * worldWidth)

That way xPos is a range centered on zero with a total width of worldWidth.
Unsure if thats whats causing the Z issue.

1 Like

I attempted your ways, they didn’t work and resulted in me teleporting very inaccurately.
I’ve played with it and I found out that:

	local zPos = math.floor(pixelPosition.Y * scaleZ + 0.5)

The ‘0.5’ at the end, when changed to 160, it moves it more accurately on the Z Axis. The ‘0.5’ is half a stud.

local xPos = math.floor(pixelPosition.X * scaleX + 0.5)

Why does it work on the xPosition, assuming that the ‘0.5’ in the xPos is half a stud as well.

So this is the closest I got to a solution, producing this:

MP0lXOjI

local worldWidth = 2048
local worldHeight = 2048

local imageButton = script.Parent.ImageButton
local localPlayer = game.Players.LocalPlayer

local mouse = localPlayer:GetMouse()

--// 400x400
local imageButtonWidth = imageButton.AbsoluteSize.X
local imageButtonHeight = imageButton.AbsoluteSize.Y

imageButton.MouseButton1Down:Connect(function(x, y)
	y = y-36 -- removing offset of topbar
	
	local pos = Vector2.new(x,y) - imageButton.AbsolutePosition -- gets distance from center of frame to mouse click

	local scalex = math.clamp(pos.X/imageButtonWidth, 0, 1)-.5 -- simple method, as accurate
	local scaley = math.clamp(math.round((pos.Y/imageButtonHeight)*100)/100, 0, 1)-.5  -- rounded to produce a more stud exact result

		-- scalex and scaley basically get hte % of distance from X = 0 and Y = 0 to X = 1 and Y = 1, basically if its in the bottom right corner its X = 1, Y = 1, therefore this is the scale.

	local totele = Vector3.new(worldWidth*scalex, 0, worldWidth*scaley) -- uses the worldwidth and times it by the scale 

	print(scalex,scaley,totele) -- prints for debug

	game.Players.LocalPlayer.Character:MoveTo(totele)
end)

Tried it on different sizes and what not, it doesn’t matter what size the UI is, will still produce the same effect.

Also if clicked in the right position (due to roblox’s input being extremely inconsistent) if you click right on the exact edge of the image button, it’ll teleport you to the exact edge of the map.