Grid building different sizes

Hello! I have a working grid based building system. The problem is, I want to be able to have multiple different block sizes with different grid sizes. I got grid sizes working, but when I place something with a different grid size on top of a block, it would float and not snap correctly.

Heres my code.

local xPos, zPos, yPos
local currentPos
local pos
local grid = 4
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local inputService = game:GetService("UserInputService")
local part = game.ReplicatedStorage:WaitForChild("Blocks").Cube
local hologram = part:Clone()
local hologramState = false
local world
for i, folder in pairs(game.Workspace:GetChildren()) do
	if folder:IsA("Folder") and folder.Name == "World" then
		world = folder
	end
end

local surfaceVectors = {
	Top = Vector3.new(0, 1, 0),
	Bottom = Vector3.new(0, -1, 0),
	Left = Vector3.new(-1, 0, 0),
	Right = Vector3.new(1, 0, 0),
	Front = Vector3.new(0, 0, -1),
	Back = Vector3.new(0, 0, 1)
}

local function snap(normalID)
	local surfaceVector = surfaceVectors[normalID]
	xPos = mouse.Hit.Position.X + surfaceVector.X
	yPos = mouse.Hit.Position.Y + surfaceVector.Y
	zPos = mouse.Hit.Position.Z + surfaceVector.Z
	xPos = math.round(xPos / grid) * grid
	yPos = math.round(yPos / grid) * grid
	zPos = math.round(zPos / grid) * grid
	pos = CFrame.new(xPos, yPos, zPos)
end

local function move()
	if mouse.Target then
		if hologramState == false then
			hologram = part:Clone()
			grid = part.GridSize.Value
		end
		hologramState = true
		local blockType = Instance.new("StringValue")
		blockType.Name = "BlockType"
		blockType.Parent = hologram
		blockType.Value = part.Name
		hologram.Parent = game.Workspace
		hologram.Transparency = 0.5
		hologram.CanCollide = false
		hologram.Name = "Hologram"
		mouse.TargetFilter = hologram
		if mouse.Target then
			currentPos = mouse.Target.CFrame
		end
		snap(mouse.TargetSurface.Name)
		hologram.CFrame = pos
	else
		hologramState = false
		hologram:Destroy()
	end
end

mouse.Move:Connect(move)

mouse.Button1Down:Connect(function()
	for i, block in pairs(world:GetChildren()) do if hologram.CFrame == block.CFrame and hologram.BlockType.Value == block.Name then return end end
	local newPart = part:Clone()
	newPart.Parent = world
	newPart.CFrame = hologram.CFrame
	wait()
	move()
	part = game.ReplicatedStorage:WaitForChild("Blocks").Pole
end)

Screenshot (114)

I advise u to watch the tutorial on how to make a placement system in roblox studio, there many content about this. change this function i assume this will fix ur problem with snapping
if grid value is set correctly

local function snap(normalID)
	local surfaceVector = Vector3.FromNormalId(Enum.NormalId[normalID])
	xPos = mouse.Hit.Position.X + (surfaceVector.X * part.Size.X / 2)
	yPos = mouse.Hit.Position.Y + (surfaceVector.Y * part.Size.Y / 2)
	zPos = mouse.Hit.Position.Z + (surfaceVector.Z * part.Size.Z / 2)
	
	xPos = math.floor(xPos / grid) * grid + 0.5 * grid
	yPos = math.floor(yPos / grid) * grid + 0.5 * grid
	zPos = math.floor(zPos / grid) * grid + 0.5 * grid
	
	pos = CFrame.new(xPos, yPos, zPos)
end
1 Like

It’s much better now but now the Pole block is offset by just a little bit.
Screenshot (116)

snap(mouse.TargetSurface.Name, mouse.Target)

local function snap(normalID, targetpart)
	local surfaceVector = Vector3.FromNormalId(Enum.NormalId[normalID])
	local surfacetargetpart = targetpart.Position + (surfaceVector * (targetpart.Size / 2))
	xPos = surfacetargetpart.X + (surfaceVector.X * part.Size.X / 2)
	yPos = surfacetargetpart.Y + (surfaceVector.Y * part.Size.Y / 2)
	zPos = surfacetargetpart.Z + (surfaceVector.Z * part.Size.Z / 2)

	xPos = math.floor(xPos / grid) * grid + 0.5 * grid
	yPos = math.floor(yPos / grid) * grid + 0.5 * grid
	zPos = math.floor(zPos / grid) * grid + 0.5 * grid

	pos = CFrame.new(xPos, yPos, zPos)
end

idk, what are u trying to do. i made some changes to function this work without offset in snapping

So what I’m trying to do is to have the player be able to build a 4x4x4 cube in a grid of 4 studs. But also be able to have other different sized parts like a 4x2x2 part and snap it with a grid of 2 studs. The problem I had was the 4x2x2 part would not be perfectly on top of the cube. How can I fix this? I want this to be like blockate’s building system. The code you have given me didn’t work. Sorry if it is hard to understand what I am trying to do as I am not the best at explanations.

I used to have this same exact problem, and I still don’t fully understand my solution. Whatever I did, works. Here’s a function that calculates the grid size a target part has, and also how to calculate offset with the grid size so that it is aligned with the grid.

local function VectorABS(v)
	return Vector3.new(math.abs(v.x), math.abs(v.y), math.abs(v.z))
end

local function GetAABB(part)
	local abs = math.abs

	local cf = part.CFrame
	local size = part.Size
	local sx, sy, sz = size.X, size.Y, size.Z

	local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:components() 

	local wsx = abs(R00) * sx + abs(R01) * sy + abs(R02) * sz
	local wsy = abs(R10) * sx + abs(R11) * sy + abs(R12) * sz
	local wsz = abs(R20) * sx + abs(R21) * sy + abs(R22) * sz

	return Vector3.new(math.floor(wsx+0.001), math.floor(wsy+0.001), math.floor(wsz+0.001))
end

This can also be helpful for calculating positional offset which is also needed to snap to the grid perfectly.
Offset is the size that is returned subtracted by the largest block size possible and then divided by 2:

local newsize = GetAABB(CheatPart)
local newoffset = VectorABS(newsize - Vector3.new(4,4,4))/2

Hope this helps.

1 Like

Hey, can you tell me how I can implement this into my script? I don’t know how I can do that as I don’t understand what newSize and newOffset is.

Newsize is a 3d vector representing gridsize. Offset is a derivative of this. In order to use this function you must have a 3d grid instead of a single number. You can also store offset somewhere because this is also needed. To snap to the grid using these two values, I use these two functions:

function numberToGrid(num, gridSizeNum, gridOffsetNum)
	return math.floor((num - gridOffsetNum)/gridSizeNum+0.5)*gridSizeNum + gridOffsetNum
end

function positionToGrid(vector, gridSize, gridOffset)
	return Vector3.new(
		numberToGrid(vector.X, gridSize.x, gridOffset.x), 
		numberToGrid(vector.y, gridSize.y, gridOffset.y), 
		numberToGrid(vector.z, gridSize.z, gridOffset.z)
	)
end

numberToGrid snaps a number to the gridsize, and positionToGrid does that for a 3d vector. Whenever you want to calculate a new position, use the positionToGrid function. In your case, you should feed xPos, yPos, and zPos into the positionToGrid function as a Vector3 and then set pos to the return value. Heres an example:

-- Have grid be Vector3.new(4,4,4) in your case, and calculate
-- offset using grid with the function I gave you earlier
local function snap(normalID)
	local surfaceVector = surfaceVectors[normalID]
	xPos = mouse.Hit.Position.X + surfaceVector.X
	yPos = mouse.Hit.Position.Y + surfaceVector.Y
	zPos = mouse.Hit.Position.Z + surfaceVector.Z

    local snappedPos = positionToGrid(Vector3.new(xPos, yPos, zPos), grid, offset)
	pos = CFrame.new(snappedPos)
end

If you change the block size you need to recaculate the gridsize and offset or it will not work.

1 Like

I am very sorry but I still do not understand how I can put this into my script. How do I get the offset? The AABB and the ABS code thing? Or something else? Please tell me what I have to do. If I am wasting your time, I am very sorry.

Ok lets say you have your two values that matter. GridSize and Offset. GridSize is a Vector3 representing the size of your grid. Offset is a calculated value that is from GridSize which represents how much the position offset should be. What you need to do is get these two values by calculating them using the GetAABB function. Put your “Hologram” part into this function, and it will output a GridSize. Then, using what I gave you to calculate offset “local newoffset = VectorABS(newsize - Vector3.new(4,4,4))/2”, make a variable and set the offset to that. Then, use this snap function which is the one I modified to work for you

local function snap(normalID, grid, offset)
	local surfaceVector = surfaceVectors[normalID]
	xPos = mouse.Hit.Position.X + surfaceVector.X
	yPos = mouse.Hit.Position.Y + surfaceVector.Y
	zPos = mouse.Hit.Position.Z + surfaceVector.Z

    local snappedPos = positionToGrid(Vector3.new(xPos, yPos, zPos), grid, offset)
	pos = CFrame.new(snappedPos)
end

What this function is doing is it is using the positionToGrid function to round your position to the grid size. Pass in your grid variable and offset variable to this as shown. Now once you have that pos, you can set your Hologram block to that pos. Then you’re done! This is a sample script so you have a general idea of what to do. This may not work exactly.

local function VectorABS(v)
	-- gets the absolute value of a Vector3
	return Vector3.new(math.abs(v.x), math.abs(v.y), math.abs(v.z))
end

local function GetAABB(part)
	-- gets the grid size of the part by checking the boundings of it
	local abs = math.abs

	local cf = part.CFrame
	local size = part.Size
	local sx, sy, sz = size.X, size.Y, size.Z

	local x, y, z, R00, R01, R02, R10, R11, R12, R20, R21, R22 = cf:components() 

	local wsx = abs(R00) * sx + abs(R01) * sy + abs(R02) * sz
	local wsy = abs(R10) * sx + abs(R11) * sy + abs(R12) * sz
	local wsz = abs(R20) * sx + abs(R21) * sy + abs(R22) * sz

	return Vector3.new(math.floor(wsx+0.001), math.floor(wsy+0.001), math.floor(wsz+0.001))
end

function numberToGrid(num, gridSizeNum, gridOffsetNum)
	return math.floor((num - gridOffsetNum)/gridSizeNum+0.5)*gridSizeNum + gridOffsetNum
end

function positionToGrid(vector, gridSize, gridOffset)
	return Vector3.new(
		numberToGrid(vector.X, gridSize.x, gridOffset.x), 
		numberToGrid(vector.y, gridSize.y, gridOffset.y), 
		numberToGrid(vector.z, gridSize.z, gridOffset.z)
	)
end

local xPos, zPos, yPos
local currentPos
local pos
local GridSize = Vector3.new(4,4,4)
local Offset = VectorABS(GridSize - Vector3.new(4,4,4))/2
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local inputService = game:GetService("UserInputService")
local part = game.ReplicatedStorage:WaitForChild("Blocks").Cube
local hologram = part:Clone()
local hologramState = false
local world
for i, folder in pairs(game.Workspace:GetChildren()) do
	if folder:IsA("Folder") and folder.Name == "World" then
		world = folder
	end
end

local surfaceVectors = {
	Top = Vector3.new(0, 1, 0),
	Bottom = Vector3.new(0, -1, 0),
	Left = Vector3.new(-1, 0, 0),
	Right = Vector3.new(1, 0, 0),
	Front = Vector3.new(0, 0, -1),
	Back = Vector3.new(0, 0, 1)
}

local function snap(normalID, grid, offset)
	local surfaceVector = surfaceVectors[normalID]
	xPos = mouse.Hit.Position.X + surfaceVector.X
	yPos = mouse.Hit.Position.Y + surfaceVector.Y
	zPos = mouse.Hit.Position.Z + surfaceVector.Z

	-- snap to grid
	local snappedPos = positionToGrid(Vector3.new(xPos, yPos, zPos), grid, offset)

	-- set new position
	pos = CFrame.new(snappedPos)
end

local function move()
	if mouse.Target then
		if hologramState == false then
			-- Change part.GridSize.Value so it is a Vector3
			-- calculate new gridsize and offset
			hologram = part:Clone()
			GridSize = part.GridSize.Value
			Offset = VectorABS(GridSize - Vector3.new(4,4,4))/2
		end
		hologramState = true
		local blockType = Instance.new("StringValue")
		blockType.Name = "BlockType"
		blockType.Parent = hologram
		blockType.Value = part.Name
		hologram.Parent = game.Workspace
		hologram.Transparency = 0.5
		hologram.CanCollide = false
		hologram.Name = "Hologram"
		mouse.TargetFilter = hologram
		if mouse.Target then
			currentPos = mouse.Target.CFrame
		end
		snap(mouse.TargetSurface.Name, GridSize, Offset)
		hologram.CFrame = pos
	else
		hologramState = false
		hologram:Destroy()
	end
end

mouse.Move:Connect(move)

mouse.Button1Down:Connect(function()
	for i, block in pairs(world:GetChildren()) do if hologram.CFrame == block.CFrame and hologram.BlockType.Value == block.Name then return end end
	local newPart = part:Clone()
	newPart.Parent = world
	newPart.CFrame = hologram.CFrame
	wait()
	move()
	part = game.ReplicatedStorage:WaitForChild("Blocks").Pole
end)

Remember: for the GridSize value in each of your blocks make it a Vector3Value instead of a NumberValue.

2 Likes

I used the code, but the problem with the Pole being offset came back.
Screenshot (117)

I got it to work when I set the Pole grid to be 2,2,4! I’ll test a slab now.

Thank you so much!!! It works! Please reply to me and tell me if you would like to be credited!!! :smiley:

Nope, no credit needed! Enjoy.

1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.