Grid Placement Position Clamp System is off when 4 does not go into base part size

Okay, so basically I’m working on a grid placement game but the clamping is wrong for anything that’s not 4x4.
For 4x4 objects (which is just the base, which is just the selection box thing), it’s all fine and dandy.

Basically, I’ve devised a little system that makes the offset of the x or z direction of the clamp that turns on if the baseplate has an odd position, like -1, 17.
Have a look.

However, as soon as I use a 2x8 base object, things start getting a bit weird.

As you can see here, the position clamping for the 2x8 is a bit off, and I'm quite unsure what to do.
local rs = game.ReplicatedStorage
local localplayer = game.Players.LocalPlayer
local rotation = 0
local roundmodule = require(rs.ModuleScripts.Round)
	if input.KeyCode == Enum.KeyCode.R then
		if rotation>=4 then
			rotation = 0
			rotation = rotation+1
	if script.Parent.Build.Value==true then
		if not game.Workspace.Temp:FindFirstChild(script.Parent.Obj.Value) then
			rs[script.Parent.Type.Value][script.Parent.Obj.Value]:Clone().Parent = game.Workspace.Temp
			localplayer:GetMouse().TargetFilter = game.Workspace.Temp[script.Parent.Obj.Value]
			local xesggo = 0
			local zesggo = 0
			local xer = 0
			local zer = 0
			if (rotation%2==0) then
				xer = game.Workspace.Temp[script.Parent.Obj.Value].PrimaryPart.Size.X
				zer = game.Workspace.Temp[script.Parent.Obj.Value].PrimaryPart.Size.Z
				xer = game.Workspace.Temp[script.Parent.Obj.Value].PrimaryPart.Size.Z
				zer = game.Workspace.Temp[script.Parent.Obj.Value].PrimaryPart.Size.X
			local xes = math.clamp(localplayer:GetMouse().Hit.p.X,game.Workspace.Grounds.Position.X-game.Workspace.Grounds.Size.X/2+xer/2,game.Workspace.Grounds.Position.X+game.Workspace.Grounds.Size.X/2-xer/2)
			local zes = math.clamp(localplayer:GetMouse().Hit.p.Z,game.Workspace.Grounds.Position.Z-game.Workspace.Grounds.Size.Z/2+zer/2,game.Workspace.Grounds.Position.Z+game.Workspace.Grounds.Size.Z/2-zer/2)
			if not (game.Workspace.Grounds.Position.X % 2 == 0) then
				xesggo = -1
			if not (game.Workspace.Grounds.Position.Z % 2 == 0) then
				zesggo = -1
			local cf =,2)+xesggo,game.Workspace.Grounds.Position.Y+game.Workspace.Grounds.Size.Y/2+game.Workspace.Temp[script.Parent.Obj.Value].PrimaryPart.Size.Y/2,roundmodule.round(zes,2)+zesggo) * CFrame.fromEulerAnglesXYZ(0, math.rad(rotation*90), 0)
		localplayer:GetMouse().TargetFilter = nil

Here is my placement code, which is a localscript, and the only script involved in the placement system. The round function derives from a module, and the function works as rounding the first argument to the second argument, which simplified is just the first argument is the number to be rounded, and the second argument is the interval at which the first argument is to be to.


You may want to look at this tutorial to see what you’re doing wrong: Creating A Furniture Placement System

1 Like

That was the post I was basing my placement system off of. Because it was made in 2018, some of the methods EgoMoose mentioned are now deprecated. I am using a simplified version of what EgoMoose did because I don’t know how and don’t have the time to use things like metatables or fancy cframe equations. I just want a working system and mine is really close to achieving that.

1 Like

In order to achieve what you are asking to do, you may have to go that route. You’ll need to use CFrames and Metatables to fix your issue. I wouldn’t know how to, though.

1 Like

No, I’m sure you don’t have to use metatables. As for CFrames, my placement system already has CFrames. It’s hard to imagine one without it. All I need is a logical mathematical solution to my clamp issue, which is the main point of why I made this thread.

1 Like

The best way i would do a snap mechanism is like this:

local nearestSnap = 4
local object = workspace.Temp[script.Parent.Obj.Value]

function roundToNearest(x)
     return nearestSnap * math.floor(x / nearestSnap + 0.5)

    local mousePos = mouse.Hit.p

    local snappedPosition =

    local snappedCF = CFrame.lookAt(snappedPosition, snappedPosition + camera.CFrame.LookVector) -- so the camera rotation is taken into account


And if you want to find snapped rotation, use my function:

	local function findClosestAxisAndVectorOfCFrameToLookVector(cframe, vector) -- here you would plug in the object's CFrame
		local vws_z = cframe:VectorToWorldSpace(, 0, 1))
		local vws_x = cframe:VectorToWorldSpace(, 0, 0))
		local vws_y = cframe:VectorToWorldSpace(, 1, 1))

		local vws_iz = -vws_z
		local vws_ix = -vws_x
		local vws_iy = -vws_y

		local z = vws_z:Dot(vector)
		local x = vws_x:Dot(vector)
		local y = vws_y:Dot(vector)

		local iz = vws_iz:Dot(vector)
		local ix = vws_ix:Dot(vector)
		local iy = vws_iy:Dot(vector)

		local largest = math.max(x, y, z, ix, iy, iz)

		return largest == x and {axis = "X", vector = vws_x}
			or largest == y and {axis = "Y", vector = vws_y}
			or largest == z and {axis = "Z", vector = vws_z}
			or largest == ix and {axis = "-X", vector = vws_ix}
			or largest == iy and {axis = "-Y", vector = vws_iy}
			or largest == iz and {axis = "-Z", vector = vws_iz}

if you want to find the world space vector output’s relation to the part’s CFrame (aka LookVector, RightVector, UpVector, etc.), do this:

local results = findClosestAxisAndVectorOfCFrameToLookVector(object.CFrame, camera.CFrame.LookVector)
local objectSpaceVector = object.CFrame:VectorToObjectSpace(results.vector)


I appreciate your insight on this topic but unfortunately I am unable to understand any of your concepts located in your scripts. I would like to use my own functions and scripts and improve on it to make it work.
Right now, my problem is in the original post. I believe it is only a matter of mathematical precision when it comes to calculating where to place the model. There is nothing wrong with my snapping, it’s just something is wrong with my grid clamping.

- Best regards, iSyriux

1 Like

To further add on, the clamping is absolutely perfect for any model that has a base that has intervals of 4. 4x8, 8,8, 16,4 ect.

Problems only arise when it has intervals of 2, which I want to be the minimum interval.


Further mathematical tests have proven that my clamping system is completely incompatible with anything that’s not intervals of four. Help me fix this, my code is dumped in the OP


Here is example of the clamp not working with anything that is not interval of 4. Here is a base that is 2x4.


To further your understanding of my issue here, you should know that this is not a problem with rotation clamping, because I have already have that figured out.

As you can see here, this is a 16x2 base. 16 is a interval of 4 and therefore works fine, but the 2 is not, and it will cause some clamping issues. By comparison, here is a 32x8

Ok I think i may have the solution, in the OP i see that you use the model’s PrimaryPart in the equation, so what i recommend to you is that you create a primary part that is 4x4 studs (or whatever works with your system) and make it invisible and noncancollide.

I would honestly prefer a solution based more around strict reliable math equations, but unfortunately i do not understand your code very well since i am only an bystander looking into the code itself.

Tell me how this goes!

Unfortunately, I would like to have the grid snap to 2 studs, instead of 4.

Change it to 2x2 then, tell me how that goes.

Change what to 2x2?

The primarypart size

this is enough characters


Unfortunately, it does not seem as though you have thoroughly read the entire post. I have tried many times experimenting with the size of the primary part, including 2x2.

I think the issue might be that you need to make the increments all 1x1, then that way all integer stud sizes will work with it

Although this solves the position clamp system and makes me realise that the clamp system has nothing to do with my problem because this is solely an issue with the rounding function, this makes the grid increment 1, which is not what I want. I need it to be 2.

Is anyone going to help me? It’s been 12 days and I haven’t recieved a single tangible answer