Get position on SurfaceGUI from Vector3 (HELP)

I’d like to make an imagelabel on a part, on a specific position, I have the position from a raycast, but of course, SurfaceGUIs dont work with that.

Does anyone have any Idea how could I achieve this?

How are you raycasting? Because particles don’t have collisions so not sure how you’re getting that position

2 Likes

edit: previous version of module didn’t calculate the x,y values correctly, heres the fixed one

i’d also like to add that this works optimally when the part isn’t rotated, which in your case, it isn’t

local WorldToVector2 = {}
function WorldToVector2:Vector2FromRay(result : RaycastResult) : (number,number,Enum.NormalId)
	-- the following will be done:
	-- convert to object space for easier cacluations
	-- use the normal values to determine which face the ray hit and cacluate the normalized distances between the corners
	-- of the hit face

	local hit_part : BasePart = result.Instance :: BasePart
	local pos : CFrame = hit_part.CFrame:ToObjectSpace(CFrame.new(result.Position + Vector3.new(0.5,0.5)))

	local norm_x = result.Normal.X
	local norm_y = result.Normal.Y
	local norm_z = result.Normal.Z
	
	if math.abs(norm_x) == 1 then
		local x = (pos.Z * -norm_x) + (hit_part.Size.Z/2)
		local y = (-pos.Y) + (hit_part.Size.Y/2)

		x /= hit_part.Size.Z
		y /= hit_part.Size.Y
		return x,y, norm_x < 0 and Enum.NormalId.Left or Enum.NormalId.Right
	end

	if math.abs(result.Normal.Y) == 1 then
		local x = (-pos.X * -norm_y) + (hit_part.Size.X/2)
		local y = (-pos.Z) + (hit_part.Size.Z/2)

		x /= hit_part.Size.X
		y /= hit_part.Size.Z
		return y,x, norm_y < 0 and Enum.NormalId.Bottom or Enum.NormalId.Top
	end

	if math.abs(result.Normal.Z) == 1 then
		local x = (pos.X * norm_z) + (hit_part.Size.X/2)
		local y = (-pos.Y) + (hit_part.Size.Y/2)

		x /= hit_part.Size.X
		y /= hit_part.Size.Y

		return x,y, norm_z < 0 and Enum.NormalId.Front or Enum.NormalId.Back
	end

end

i’ve created this short module to determine the normalized distances between the corners of the raycast’s hit face

to get the 2d position:

local nx,ny,face = WorldToVector2:Vector2FromRay(raycast)
local x,y = SurfaceGui.CanvasSize.X * nx, SurfaceGui.CanvasSize.Y * ny
1 Like

It is not a particle. just a sphere controlled through a script.

2 Likes

Holy crap, well first of all, how would I use this?

And by any chance could you explain some of the math you’re doing here? My brain is fried just from reading that.

Edit: I’m using the normal of the raycast to find the face of the part, is there any way to use this in the function? Because I had intended the surfaceGUI to also go on other player’s bodyparts, so normally they’d be rotated.

1 Like
local WorldToVector2 = {}
function WorldToVector2:Vector2FromRay(result : RaycastResult) : (number,number,Enum.NormalId)
	-- the following will be done:
	-- convert to object space for easier cacluations
	-- use the normal values to determine which face the ray hit and cacluate the normalized distances between the corners
	-- of the hit face

	local hit_part : BasePart = result.Instance :: BasePart
	local pos : CFrame = hit_part.CFrame:ToObjectSpace(CFrame.new(result.Position))
	local local_normal : Vector3 = hit_part.CFrame:VectorToObjectSpace(result.Normal)
	
	local norm_x = math.round(local_normal.X)
	local norm_y = math.round(local_normal.Y)
	local norm_z = math.round(local_normal.Z)

	if math.abs(norm_x) == 1 then
		local x = (-pos.Z  * norm_x) + (hit_part.Size.Z/2)
		local y = (-pos.Y) + (hit_part.Size.Y/2)

		x /= hit_part.Size.Z
		y /= hit_part.Size.Y
		return x,y, norm_x < 0 and Enum.NormalId.Left or Enum.NormalId.Right
	end

	if math.abs(norm_y) == 1 then
		local x = (-pos.X  * -norm_y) + (hit_part.Size.X/2)
		local y = (-pos.Z) + (hit_part.Size.Z/2)

		x /= hit_part.Size.X
		y /= hit_part.Size.Z
		return y,x, norm_y < 0 and Enum.NormalId.Bottom or Enum.NormalId.Top
	end

	if math.abs(norm_z) == 1 then
		local x = (-pos.X  * -norm_z) + (hit_part.Size.X/2)
		local y = (-pos.Y) + (hit_part.Size.Y/2)

		x /= hit_part.Size.X
		y /= hit_part.Size.Y

		return x,y, norm_z < 0 and Enum.NormalId.Front or Enum.NormalId.Back
	end

end

i forgot to use VectorToObjectSpace for the raycast’s normal which pretty much fixes the rotation problem

the module already returns the face of the part for you

Wow, it works, It’s gonna take me a while to understand all this witchery.

One last question tho, any way to keep the texture a fixed size? the image label im using uses offset instead of scale for size yet it still changes depending on the size of the part its parented to.

you can try changing the CanvasSize to see if that helps

Gui.CanvasSize *= ray.Instance.Size.Magnitude * 0.025

Seems to have worked, thanks!