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.
EDIT:
I found out some time after making this that the previous response that i gave is NOT the best solution for position snapping. At the time I made do with it, but i came across the issues again in a later project that I worked on, so I made a couple of new functions which work a bit better.
I was also asked whether these work with any snap increment, so my answer is yes. The previous versions should work with most snapping increments, but may behave a bit odd with some increments, and the new functions that I am giving work with any snap increment as far as I am aware. (at any number least within roblox’s capabilities).
The issue with the previous functions that I gave is that they sometimes result in the part clipping below the hit position, so I made two new functions, one which positions the part directly on the hit surface and removes the vertical snapping, essentially only snapping the part along a 2D grid rather than a 3d grid, and the other which snaps the part to the nearest snap position which is above the hit surface.
-- Properly snaps to a 3d grid without the part clipping into the ground
function Snap3D(HitPosition:Vector3,SnapAmount:number,BlockSize:Vector3,HitNormal:Vector3)
local NewSize = Vector3.new(
math.max(BlockSize.X/2,SnapAmount),
math.max(BlockSize.Y/2,SnapAmount),
math.max(BlockSize.Z/2,SnapAmount)
)
local GridSize = HitNormal*NewSize
local BlockPosition:Vector3 = HitPosition+GridSize
local X = math.ceil((BlockPosition.X-(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the x Axis
local Y = math.ceil((BlockPosition.Y-(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the y Axis
local Z = math.ceil((BlockPosition.Z-(SnapAmount/2))/SnapAmount)*SnapAmount -- Gets the position snapped to the snap amount on the z Axis
if not X or X ~= X then
X = BlockPosition.X
end
if not Y or Y ~= Y then
Y = BlockPosition.Y
end
if not Z or Z ~= Z then
Z = BlockPosition.Z
end
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
-- Snaps the part to the surface
-- Downside: only snaps on the x axis relative to the part (side to side) on sloped surfaces
function surfaceSnap(HitPosition:Vector3,SnapAmount:number,BlockSize:Vector3,HitNormal:Vector3)
local BlockPosition:Vector3 = HitPosition+(HitNormal*(BlockSize/2))
local GridSize_X = math.round((HitNormal.Magnitude-math.abs(math.round(HitNormal.X))))*SnapAmount
local GridSize_Y = math.round((HitNormal.Magnitude-math.abs(math.round(HitNormal.Y))))*SnapAmount
local GridSize_Z = math.round((HitNormal.Magnitude-math.abs(math.round(HitNormal.Z))))*SnapAmount
local X = math.round(BlockPosition.X/GridSize_X)*GridSize_X
local Y = math.round(BlockPosition.Y/GridSize_Y)*GridSize_Y
local Z = math.round(BlockPosition.Z/GridSize_Z)*GridSize_Z
if not X or X ~= X then
X = BlockPosition.X
end
if not Y or Y ~= Y then
Y = BlockPosition.Y
end
if not Z or Z ~= Z then
Z = BlockPosition.Z
end
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