How would I get the face of a part that the mouse is pointing at?

How would I get the face of a part that the mouse is pointing at?

1 Like

Using UIS

local uis = game:GetService("UserInputService")
local maxRayDistance = 1000

--[[
* Fetches the normal vector from a raycast
* to the mouse from the mouse's screen position
* @param {number} x The mouse X position
* @param {number} y The Mouse Y position
* @returns {Vector3|nil} Returns a Vector3 or nil if no normal
]]
local function getRayCollisionNormal(x,y)
	local cam = workspace.CurrentCamera
	local unitRay = cam:ScreenPointToRay(x,y)
	local rayResult = workspace:Raycast(unitRay.Origin, unitRay.Direction * maxRayDistance)
	if (rayResult) then
		local instance = rayResult.Instance
		local normal = rayResult.Normal
		
		-- Get an object-space normal for rotated instance
		if (instance) then
			normal = instance.CFrame:VectorToObjectSpace(normal)
		end
		
		return normal or nil;
	end
end

uis.InputChanged:Connect(function(io)
	if (io.UserInputType == Enum.UserInputType.MouseMovement) then
		local hitNormal = getRayCollisionNormal(io.Position.X, io.Position.Y)
		local surface = nil;
		
		if (hitNormal) then
			local dotY = hitNormal:Dot(Vector3.new(0,1,0))
			local dotX = hitNormal:Dot(Vector3.new(1,0,0))
			local dotZ = hitNormal:Dot(Vector3.new(0,0,1))
			if (math.abs(dotY) >= 0.9) then
				if (dotY < 0) then
					surface = Enum.NormalId.Bottom
				else
					surface = Enum.NormalId.Top
				end
			elseif (math.abs(dotX) >= 0.9) then
				if (dotX < 0) then
					surface = Enum.NormalId.Left
				else
					surface = Enum.NormalId.Right
				end
			else
				if (dotZ < 0) then
					surface = Enum.NormalId.Front
				else
					surface = Enum.NormalId.Back
				end
			end
		end

		-- surface variable holds either nil or the NormalId enum of the mouse's hit surface
		print(surface)
	end
end)

Edit: Adjusted code to take into account rotation.

4 Likes

If you’d like to take the simple route, you could use the PlayerMouse instance obtained from Player::GetMouse and utilise its TargetSurface/Target properties.

ie.

--// LocalScript
local player = game:GetService("Players").LocalPlayer;
local mouse = player:GetMouse();

local surface = mouse.TargetSurface;
local target = mouse.Target;

Keep in mind that the PlayerMouse instance is superseded by UserInputService due to more customisation, however it’ll suffice for small tasks.

9 Likes

Thankyou appreciate this, this answer is alot better if you just need the normal because casting a extra ray is a pain if you can literally use one variable to get the Surface Normal.

1 Like