Clipping issue with grid placement system

Hey, I’m currently working on a game that requires a placement system which is locked to a grid of 3 studs, but I’m facing a very annoying issue that I have not been able to fix. The issue is that if I point my mouse on the side of another block, it clips into that block, while what I want it to do, is pivot to the position (on the grid) next to that block, on the side that my mouse is (sorry for the bad explanation, I don’t know how to say it.)

local rep = game:GetService("ReplicatedStorage")
local selectionBox = rep.Modules.PlacingSystems.SelectionBox

-- grid
local GRID_SIZE = 3

function module.Place(plr, item)
	if not (plr and item) then return end

	local itemFolder = rep.Items.InventoryItems:FindFirstChild(item)
	local placeable = itemFolder.Information.Placeable.Value
	if not placeable then return end

	local model = itemFolder.Model:FindFirstChildWhichIsA("Model")
	if not (model and model.PrimaryPart) then return end

	local mouse = plr:GetMouse()
	local ghost = model:Clone()
	ghost.Parent = workspace

	for i, part in pairs(ghost:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Transparency = 0.5
			part.CanCollide = false
		end
	end

	mouse.TargetFilter = ghost

	mouse.Move:Connect(function()
		local MousePos = mouse.Hit.Position
		local BlockPos = MousePos

		BlockPos = BlockPos + Vector3.new(0, model.PrimaryPart.Size.Y / 2, 0)

		local XPos = math.round(BlockPos.X / GRID_SIZE) * GRID_SIZE
		local ZPos = math.round(BlockPos.Z / GRID_SIZE) * GRID_SIZE
		BlockPos = Vector3.new(XPos, BlockPos.Y, ZPos)

		ghost:PivotTo(CFrame.new(BlockPos))
	end)
end

return module

Any help is appreciated

I’m talking about the transparent part clipping by the way. I have not been able to make it place the block yet, because I want to fix this issue first

Seems you need:

  1. detect if anything overlaps with the ghost at BlockPos
  2. implement logic to find a position near BlockPos that does not overlap with anything and fits into the grid

This is largely a matter of preference when building a system like that. Many go with it so you can integrate it into builds. A more common approach is to allow movement freely but prevent placement where objects would clip into other parts. Usually, with this approach, the object turns red to show it cannot be placed there. I prefer this method, with the object visually snapping into place, because it lets you move the build item freely while constructing, and the more you add to a build, the more convenient this becomes. You may want to reconsider your direction here.

1 Like

Add a small surface bias before rounding the position, something like this should work:

local Surface = mouse.TargetSurface

BlockPos += Vector3.FromNormalId(Surface) * 0.1

This solution works for blocks that do not have a rotation, but a more generalized solution would be using RaycastResult.Normal (multiplied by some very small value, like you did with 0.1, but it can be much smaller like 10^-6). This is used to offset the position of the mouse (RaycastResult.Position + RaycastResult.Normal*(10^-6)) slightly, so that, instead of being perfectly in between two grid positions, it goes into the adjacent grid position

However, using the normal can only be done when using local RaycastResult = workspace:Raycast(...), as the Mouse object doesn’t provide the normal of the hit surface. For more information, you can look at this solution from another thread

1 Like

I noticed your grid is set to 3. One simple approach to these build systems is to have your placed objects, like walls, be twice the length of the grid size and to avoid using an odd number for the grid itself. Clipping issues don’t automatically occur when you move a wall next to other walls, and the overall math works out nicely in even chunks, whether whole or split. A grid size of 4 is a good choice for many reasons. A wall with an 8-stud length moves right into place with the existing math, with no modifications needed.

Timing favors 3s, building favors 4s.
(naturally complements computational harmony)