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

Sorry Im a bit late but If you still want some help with this here is a bit of code I just made for my building game. Even if ya don’t need it Ima still post it for those who might.

function Snap(HitPosition:Vector3,SnapAmount:number,BlockSize:Vector3,HitNormal:Vector3)
	local NewPos = HitPosition+(HitNormal*(BlockSize/2)) -- Gets the new position of a block
	local X = math.floor((NewPos.X+(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the x Axis
	local Y = math.floor((NewPos.Y+(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the y Axis
	local Z = math.floor((NewPos.Z+(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the z Axis
	local NewBlockPos = Vector3.new(math.round(X*1000)/1000,math.round(Y*1000)/1000,math.round(Z*1000)/1000) -- Removes floating point imprecision.
	return NewBlockPos
end

Also Here is a version for purely snapping to a Position without calculating a block’s new position

function Snap(Position:Vector3,SnapAmount:number)
	local X = math.floor((Position.X+(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the x Axis
	local Y = math.floor((Position.Y+(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the y Axis
	local Z = math.floor((Position.Z+(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the z Axis
	local NewBlockPos = Vector3.new(math.round(X*1000)/1000,math.round(Y*1000)/1000,math.round(Z*1000)/1000) -- Removes floating point imprecision.
	return NewBlockPos
end

Note: The math.round(Number*1000)/1000 bit is to remove any floating point imprecision, it is not 100% needed if you don’t want it. Also 1000 can be made bigger to allow a higher decimal count.

Also the reason your first attempt didn’t work is most likely because math.floor will cause the position to be slightly offset which is what the +(SnapAmount/2) does is to eliminate that offset.

Another Alternative to math.floor((Position.X(SnapAmount/2))/SnapAmount)*SnapAmount is
math.round(Position.X/SnapAmount)*SnapAmount but I dont recomment it because it breaks a bit in some parts of the world.

Hopefully this actually works and I didn’t make a mistake while editing it.