Block grid placement not working properly

Can someone help me with this?

The block is not perfectly aligned to the grid. When I place a block on top of another block, it works fine but not with slabs.

But when I try placing a block on the side of another block, the preview starts flickering (it keeps switching very fast between being slightly in front of the target block and being inside it).
It only happens when placing blocks on the sides of other blocks, not on top or bottom.
Does anyone know how to fix it?

Honestly, my script looks pretty messy:

local GridSize = 4

local gridOffsets = {
	[1] = {
		Offset = 0.5,
	},
	[2] = {
		Offset = 0.5,
	},
	[4] = {
		Offset = 0.5,
	},
}

local function getBlockPosition(vector3, blockSize, oX, oY, oZ)
	local half = blockSize/2

	return Vector3.new(
		math.floor((vector3.X + half.X) / blockSize.X) * blockSize.X + oX,
		math.floor((vector3.Y + half.Y) / blockSize.Y) * blockSize.Y + oY,
		math.floor((vector3.Z + half.Z) / blockSize.Z) * blockSize.Z + oZ
	)
end

local castParams = RaycastParams.new()
	castParams.FilterType = Enum.RaycastFilterType.Exclude
	castParams.FilterDescendantsInstances = {Player.Character}
	
	local ovParams = OverlapParams.new()
	ovParams.FilterType = Enum.RaycastFilterType.Exclude
	ovParams.FilterDescendantsInstances = {self.PreviewModel, Player.Character}

RunService:BindToRenderStep("Preview", Enum.RenderPriority.Camera.Value, function()
	if BuildingController.CurrentSelected == self then
		local mouseLocation = UserInputService:GetMouseLocation()
		local unityRay = workspace.CurrentCamera:ViewportPointToRay(mouseLocation.X, mouseLocation.Y)
		local cast = workspace:Raycast(unityRay.Origin, unityRay.Direction * 1000, castParams)

		if cast then
			local Size = self.PrimaryPart.Size

			local sizeX = Size.X
			local sizeY = Size.Y
			local sizeZ = Size.Z

			local surfaceX = math.round(cast.Normal.X)
			local surfaceY = math.round(cast.Normal.Y)
			local surfaceZ = math.round(cast.Normal.Z)

			local targetSize = cast.Instance.Size

			local scale = gridOffsets[GridSize].Scale and gridOffsets[GridSize].Scale*2 or 2

			if surfaceX == 1 then
				offsetX = sizeX/2 - scale
			elseif surfaceX == -1 then
				offsetX = -sizeX/2 + scale
			end

			if surfaceY == 1 then
				offsetY = sizeY/2
			elseif surfaceY == -1 then
				offsetY = -sizeY/2
			end

			if surfaceZ == 1 then
				offsetZ = sizeZ - scale
			elseif surfaceZ == -1 then
				offsetZ = -sizeZ/2 + scale
			end

			local overlapBool = false

			local pos = getBlockPosition(cast.Position, Size, offsetX, offsetY, offsetZ)

			self.LastValidCFrame = self.LastValidCFrame or self.PrimaryPart.CFrame

			local targetCF = CFrame.new(pos) * CFrame.Angles(math.rad(self.y_Angle), math.rad(self.x_Angle), 0)

			local ovParams = OverlapParams.new()
			ovParams.FilterType = Enum.RaycastFilterType.Exclude
			ovParams.FilterDescendantsInstances = { self.PreviewModel, Player.Character }

			local hitbox = self.PreviewModel:FindFirstChild("Hitbox")

			local prevHitboxCF = hitbox.CFrame
			local prevPrimaryCF = self.PrimaryPart.CFrame

			hitbox.CFrame = targetCF

			local overlappingParts = workspace:GetPartsInPart(hitbox, ovParams)

			if #overlappingParts > 0 then
				local lastPos = self.LastValidCFrame.Position
				local currentPos = targetCF.Position
				local direction = Vector3.new(0, currentPos.Y - lastPos.Y, 0)

				local step = 0.1
				local safePos = lastPos

				for i = 1, 30 do
					local testPos = safePos + direction.Unit * step
					hitbox.CFrame = CFrame.new(testPos)

					local parts = workspace:GetPartsInPart(hitbox, ovParams)
					if #parts > 0 then break end
					safePos = testPos
				end

				local finalCF = CFrame.new(safePos) * CFrame.Angles(math.rad(self.y_Angle), math.rad(self.x_Angle), 0)
				self.PrimaryPart.Parent:SetPrimaryPartCFrame(finalCF)
				hitbox.CFrame = finalCF
				self.LastValidCFrame = finalCF
			else
				self.PrimaryPart.Parent:SetPrimaryPartCFrame(targetCF)
				self.LastValidCFrame = targetCF
			end
		end
	end
end)
2 Likes

You write down castParams, but don’t define them. The glitching that it’s doing looks to me like a result of the cast interacting with the ghost object. Make sure to ignore it. As for the alignment, check this out Weird offset in a building system - #4 by ExercitusMortem

The castParams and ovParams are defined, i will edit this post

Include the PreviewModel in the castparams filter

Do i need to replace the getBlockPosition function with the code you sent?