GUIPositionToWorldPosition

I got asked to write this script for someone and figured that other people might potentially want it if they run into the issue. It isn’t perfect code, but it works for the situation and usecase. Free to use without attribution.

--!strict
local module = {}

function module.GUIPositionToWorldPosition(GuiBase:GuiBase2d, Offset:Vector2): CFrame | nil
	Offset = Vector2.new(math.clamp(Offset.X+GuiBase.Anchor.X, 0, 1), math.clamp(Offset.Y+GuiBase.Anchor.X, 0, 1))
	local SurfaceGUI:SurfaceGui? = GuiBase:FindFirstAncestorOfClass("SurfaceGui")
	local BasePart:BasePart? = GuiBase:FindFirstAncestorOfClass("Part") or GuiBase:FindFirstAncestorOfClass("UnionOperation") or GuiBase:FindFirstAncestorOfClass("MeshPart")
	if not (SurfaceGUI and BasePart) then
		warn(not SurfaceGUI and not BasePart and "There is no SurfaceGUI or BasePart parented to this GuiBase" or 
			not SurfaceGUI and "There is not SurfaceGUI parented to this GuiBase." or 
			not BasePart and "There is not SurfaceGUI parented to this GuiBase.")
		return nil
	end
	local Normal = Vector3.FromNormalId(SurfaceGUI.Face)
	
	local Size = BasePart.Size
	local BasePartOffset = BasePart.Size*Normal/2
	
	local SizeOffset = Vector3.new(Normal.X == 0 and -Size.X/2 or 0,Normal.Y == 0 and -Size.Y/2 or 0,Normal.Z == 0 and Size.Z/2 or 0)
	
	return BasePart.CFrame*CFrame.new(BasePartOffset+SizeOffset)*CFrame.new(Vector3.new(-(GuiBase.AbsolutePosition.Y+GuiBase.AbsolutePosition.Y*Offset.Y)/SurfaceGUI.PixelsPerStud,0,-(GuiBase.AbsolutePosition.X+GuiBase.AbsolutePosition.X*Offset.Y)/SurfaceGUI.PixelsPerStud))
end

return module
8 Likes

Looks cool! What would it be used for?

2 Likes

I’m not exactly sure, I got asked to write it by someone else for an interaction system.

2 Likes

I’m not to sure what this is used for? Can you explain?

It converts a position on a SurfaceGUI into CFrame representing its position in 3d space. It can be used to create more complex onclick effects, handle building stuff if you don’t want to use clickdetectors, and be used for many other little applications like that.

Couldn’t you just use ScreenToWorldPoint?