Movement not going along faces perfectly

For some reason, my movement system creates a gap between the object and the floor. This gap isn’t there when on other faces however, and only occurs on the floor (top surface of an object)

I use a raycast from the mouse to get the normal the mouse is on


Upon further testing, the CFrame is perfect on walls, but it’s +0.25 on the floor, and -0.25 on the roof. So I need a way to take 0.25 from the CFrame if top surface, or add 0.25 if it’s bottom surface

--// Calculate the CFrame
local function CalculateCFrame(oldCf, yOffset, surfacePos, normal)
	local OldUpVector = oldCf.UpVector
	local OldRotation = oldCf - oldCf.Position
	local NewPosition = surfacePos + normal * yOffset
	local Dot = OldUpVector:Dot(normal)

	if Dot > 1-1e-5 then
		return OldRotation + NewPosition
	end

	if Dot < -1+1e-5 then
		return CFrame.Angles(math.pi, 0, 0) * OldRotation + NewPosition
	end

	local ScalarAngle = math.acos(Dot)
	local RotationAxis = OldUpVector:Cross(normal).Unit

	return CFrame.fromAxisAngle(RotationAxis, ScalarAngle) * OldRotation + NewPosition
end

local UnitRay = Camera:ScreenPointToRay(Mouse.X, Mouse.Y, 1)
local NewRay = workspace:Raycast(UnitRay.Origin, UnitRay.Direction * 1000, RaycastFilter)

PosX = math.floor((NewRay.Position.X / 0.5) + 0.5) * 0.5
PosY = math.floor((NewRay.Position.Y / 0.5) + 0.5) * 0.5
PosZ = math.floor((NewRay.Position.Z / 0.5) + 0.5) * 0.5

local NewPos = Vector3.new(PosX, PosY, PosZ)
Model:SetPrimaryPartCFrame(
	CalculateCFrame(
		Model.PrimaryPart.CFrame,
		Model.PrimaryPart.Size.Y / 2,
		NewPos,
		NewRay.Normal
	)
)

There is a property of the mouse called TargetSurface. Simply put, it describes what surface (top, bottom, left, right, front, back) the user is hovering on. Take note that this property is only available when there is a valid Target part, so hovering in the sky would have no TargetSurface.

I want to edit my current code, and using raycasts returns better results

You are rounding the .Y of position. This will create problems because you would need the part below it to be perfectly aligned with your snapping (0.5 studs). Simply remove the rounding on the .Y position and it will be fixed.

That fixes the problem of the gap, but then when I move the object say along the walls, it has free movement. I still want a snapped position for these positions

I ran into this problem a long time ago too. You should orient the object based on the CFrame of the target part and using :ToObjectSpace(). I forgot how I did it, but I believe I used surface normals or something. I’ll get back to you soon once I find my script.

Found my original math

local YRotation = CFrame.new(0, 0, 0, 0, 0, 1, 0, 1, 0, -1, 0, 0)
local XRotation = CFrame.new(0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 1, 0)

local origin = CFrame.new(raycastResult.Position) * CFrame.lookAt(Vector3.new(), -raycastResult.Normal) * YRotation * XRotation
local relative = origin:PointToObjectSpace(raycastResult.Position)
finalCFrame = origin * CFrame.new(math.round(relative.X), relative.Y + Size.Y, math.round(relative.Z)) * Angle

The script was super messy and sorry but not even I understood my previous code. I believe that I set the position to the ray, then oriented it with the -normal using CFrame.lookAt then multiplied it by constants to get it upright (you could probably use your CFrame.fromAxisAngle here). Then, I oriented it along with the target CFrame and finally rounded everything off at the end. The * Angle is the rotation applied when using R or T to turn the model.

You’re free to try and interpret this code if you’d like. Sorry that I couldn’t be of more help. Oh, but this code makes it so that the bottom part of the model will always align with the side of the wall the mouse is on. I had another script that would instead use the NormalId of the TargetSurface from the Ray.Hit but this did not look as nice since it only worked for cubes.

1 Like