How to snap a part to grid

I am making a game where you can build. Now one problem.

I want to make a grid thingy where the part snaps to a grid. I tried using

CFrame.new(position.X - position.X%3, position.Y - position.Y%3, position.Z - position.Z%3).Position
I also tried math.floor(X / 3) * 3 but it gives the same result

If i want to build up, then place a part on the left side or right side then only one side works, if i try placing the block it goes inside of the block, but not on the other side.

Is there any other method? How do i fix this?
some other info:
The part is a 3x3x3 and the snap value is 3, so it will snap every 3 studs
The baseplate is one single block, there’s no multiple blocks simulating a grid.
I want a snapping similar to Build A Hideout And Sword Fight’s

please help; if you need more information please tell me

1 Like

The math.floor() way should have worked. Can you show the exact code you used? Also try using math.round instead of floor, it should feel more natural.

that is the whole code, math.floor one is the exact same as the other (x - x%3)

math.floor does not give any better results

EDIT: By whole code I mean that’s the only function I used, its literally Part.Position = and the formula

I need all your code to understand the input position you’re using.

local module = {}

module.SelectedBlock = "Brick"

function module.Snap(position, ins, Object)
	if Object:IsA("BasePart") then
		--if ins.Instance.Name == "BasePlot" then
		--	return Vector3.new(position.X - position.X%3, position.Y + Object.Size.Y/2, position.Z-position.Z%3)
		--else
		--	return ins.Instance.CFrame:PointToWorldSpace(ins.Normal * 3)
		--end
		return CFrame.new(position.X - position.X%3, position.Y - position.Y%3 + Object.Size.Y, position.Z - position.Z%3).Position
	else
		--if ins.Instance.Name == "BasePlot" then
		--	return Vector3.new(position.X - position.X%3, position.Y + Object:GetExtentsSize().Y / 2, position.Z-position.Z%3)
		--else
		--	return ins.Instance.CFrame:PointToWorldSpace(ins.Normal * 3)
		--end
		return CFrame.new(position.X - position.X%3 + 1, position.Y - position.Y%3 + Object.PrimaryPart.Size.Y, position.Z - position.Z%3 + 1).Position
	end
end
function module:placeBlock(x : string, position : Vector3, ins : RaycastResult, ori : Vector3)
	local fObject = script.Parent.Parent.Items:FindFirstChild(x)
	if not fObject then return "Failed" end
	script.Parent.Parent.Construct:InvokeServer({
		TypeOfCall = "Place",
		Object = fObject.Name,
		Position = module.Snap(position, ins, fObject),
		Orientation = ori
	})
end
function module:removeBlock(block : BasePart)
	script.Parent.Parent.Construct:InvokeServer({TypeOfCall = "Remove", Object = block})
end

return module

The code in – is the one that works, but its the most non-effecient thing

You seem to have two different positions going into Snap, “position” and the raycast result’s Position (ins.Position), did you mean to use ins.Position?

No, the code i put is fully working;

The commented code is the method that works, but I said its bad, since it uses 2 different methods into one