Raycast From Center of Screen to Part Incorrect Faces

I am currently working on a small project which is a Minecraft like game and I am using raycast to detect the face that the player is clicking but it seems to only work when it’s close. It works out of first person pretty much all the time however when I go in first person, it seems to not work correctly.

It usually is offset and on the incorrect face (Example: 9 studs away and look at a part from the top says the face is on the right or front)

I have the raycast code and surface detecting code listed below:

local length = 15 --Reach distance

function getRayCollisionNormal(x,y)
	local cam = workspace.CurrentCamera
	local viewportPoint = cam.ViewportSize / 2
	local unitRay = cam:ViewportPointToRay(viewportPoint.X, viewportPoint.Y, 0)
	local rayResult = workspace:Raycast(unitRay.Origin, unitRay.Direction * length)
	
	if (rayResult) then
		local instance = rayResult.Instance
		local normal = rayResult.Normal

		-- Get an object-space normal for rotated instance
		if (instance) then
			normal = rayResult.Normal
		end

		return normal or nil;
	end
end

function GetFaceOfPart()
	local camera = workspace.CurrentCamera
	local hitNormal = getRayCollisionNormal(PlayerMouse.X, PlayerMouse.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.round((math.abs(dotY))) >= 0.9 then
			if math.round((dotY)) < 0 then
				surface = Enum.NormalId.Bottom
			else
				surface = Enum.NormalId.Top
			end
		elseif math.round((math.abs(dotX))) >= 0.9 then
			if math.round((dotX)) < 0 then
				surface = Enum.NormalId.Left
			else
				surface = Enum.NormalId.Right
			end
		else
			if math.round((dotZ)) < 0 then
				surface = Enum.NormalId.Front
			else
				surface = Enum.NormalId.Back
			end
		end
	end
	
	print(surface.Name)

	return surface
end

Much thanks to anybody that can help!

2 Likes

Your code for calculating the face of the part is incorrect. Here’s one way to do it

--Maps all 6 directions to its Normal ID
local vectorToFace = {
	[Vector3.xAxis] = Enum.NormalId.Right,
	[-Vector3.xAxis] = Enum.NormalId.Left,
	[Vector3.yAxis] = Enum.NormalId.Top,
	[-Vector3.yAxis] = Enum.NormalId.Bottom,
	[Vector3.zAxis] = Enum.NormalId.Back,
	[-Vector3.zAxis] = Enum.NormalId.Front,
}

--[[
	hitNormal Vector3 -- The normal vector of the surface that the raycast hit
	hitPart BasePart -- The BasePart that that the raycast hit
]]
local function getFaceOfPart(hitNormal: Vector3, hitPart: BasePart): Enum.NormalId
	local rotation = CFrame.lookAt(Vector3.zero, hitNormal) --Convert the normal to a CFrame rotation
	local relativeRotation = hitPart.CFrame:ToObjectSpace(rotation) --Convert the rotation to be relative to the hit part's rotation
	local relativeFaceDirection = relativeRotation.LookVector --The look vector of the CFrame now corresponds to a NormalID
	local normalId = vectorToFace[relativeFaceDirection]
	return normalId
end
1 Like

Thank you so much! It’s working properly now!

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.