Make block placement preview move in grid

so i scripted an entire block placement system, but it seems like it is facing the player, i would like it’s bottom to face the floor and move in grids, here’s the tool script:


local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local UIS = game:GetService('UserInputService')
local RS = game:GetService('ReplicatedStorage')
local tool = script.Parent
local Event = RS:WaitForChild('PlaceBlock')
local pos
equipped = false
local part = game.ReplicatedStorage.Blocks.Part:Clone()

-- Surface Detection

local function Surface(surface)
	if surface.Name == "Top" then
		pos = Vector3.new(0,4,0)
	elseif surface.Name == "Bottom" then
		pos = Vector3.new(0,-4,0)
	elseif surface.Name == "Front" then
		pos = Vector3.new(0,0,-4)
	elseif surface.Name == "Back" then
		pos = Vector3.new(0,0,4)
	elseif surface.Name == "Left" then
		pos = Vector3.new(-4,0,0)
	elseif surface.Name == "Right" then
		pos = Vector3.new(4,0,0)
	end
	return pos
end




local num = 1
local blocks = RS.Blocks:GetChildren()
local Block_Choice = blocks[1]

numberOfBlocks = #blocks


script.Parent.Activated:Connect(function()
	local target = mouse.Target
	if target then
		local surface = mouse.TargetSurface
		local pos = Surface(surface)
		Event:FireServer(target,pos,Block_Choice)
	end
end)

	-- the selection part 
mouse.Move:Connect(function()
	if equipped == true then
	
	
	part.Parent = workspace
	mouse.TargetFilter = part
		part.CFrame = mouse.Hit
		
end
end)
tool.Equipped:Connect(function()
	equipped = true
end)

tool.Unequipped:Connect(function()
	equipped = false
	
end)
3 Likes

Is it possible for you to show us a video of what’s happening.
As this is a visibility question its a bit hard to visualise from just part of the code.


as you can see, the preview part does not grid on floor unlike the placement system.


local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local UIS = game:GetService('UserInputService')
local RS = game:GetService('ReplicatedStorage')
local tool = script.Parent
local Event = RS:WaitForChild('PlaceBlock')
local pos
equipped = false
local part = game.ReplicatedStorage.Blocks.Part:Clone()

-- Surface Detection

local function Surface(surface)
	if surface.Name == "Top" then
		pos = Vector3.new(0,4,0)
	elseif surface.Name == "Bottom" then
		pos = Vector3.new(0,-4,0)
	elseif surface.Name == "Front" then
		pos = Vector3.new(0,0,-4)
	elseif surface.Name == "Back" then
		pos = Vector3.new(0,0,4)
	elseif surface.Name == "Left" then
		pos = Vector3.new(-4,0,0)
	elseif surface.Name == "Right" then
		pos = Vector3.new(4,0,0)
	end
	return pos
end




local num = 1
local blocks = RS.Blocks:GetChildren()
local Block_Choice = blocks[1]

numberOfBlocks = #blocks


script.Parent.Activated:Connect(function()
	local target = mouse.Target
	if target then
		local surface = mouse.TargetSurface
		local pos = Surface(surface)
		Event:FireServer(target,pos,Block_Choice)
	end
end)

	-- the selection part 
mouse.Move:Connect(function()
	if equipped == true then
	
	
	part.Parent = workspace
	mouse.TargetFilter = part
        local x = mouse.Hit.X
        local y = mouse.Hit.Y
        local z = mouse.Hit.Z
        part.Position = Vector3.new(math.floor(x/4) * 4, math.floor(y/4) * 4, math.floor(z/4) * 4)
end
end)
tool.Equipped:Connect(function()
	equipped = true
end)

tool.Unequipped:Connect(function()
	equipped = false
	
end)
1 Like

hmm half of it seems to be in the ground, lemme try add 2 on stuff and see if it works

edit: yep got it to work ty

2 Likes

hmm i got two problems, it somehow don’t work when i am placing on the left side, but fine on right side, second problem is that i can place 2+ blocks inside one. here is the video of the first problem


now the slightly changed script

wait()
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local UIS = game:GetService('UserInputService')
local RS = game:GetService('ReplicatedStorage')
local tool = script.Parent
local Event = RS:WaitForChild('PlaceBlock')
local pos
equipped = false
local part = game.ReplicatedStorage.Blocks.Part:Clone()

part.Click:Destroy()
-- Surface Detection

local function Surface(surface)
	if surface.Name == "Top" then
		pos = Vector3.new(0,4,0)
	elseif surface.Name == "Bottom" then
		pos = Vector3.new(0,-4,0)
	elseif surface.Name == "Front" then
		pos = Vector3.new(0,0,-4)
	elseif surface.Name == "Back" then
		pos = Vector3.new(0,0,4)
	elseif surface.Name == "Left" then
		pos = Vector3.new(-4,0,0)
	elseif surface.Name == "Right" then
		pos = Vector3.new(4,0,0)
	end
	return pos
end




local num = 1
local blocks = RS.Blocks:GetChildren()
local Block_Choice = blocks[1]

numberOfBlocks = #blocks


script.Parent.Activated:Connect(function()
	local target = mouse.Target
	if target then
		local surface = mouse.TargetSurface
		local pos = part.Position--Surface(surface)
		Event:FireServer(target,pos,Block_Choice)
	end
end)

-- the selection part 
mouse.Move:Connect(function()
	if equipped == true then

		part.CanCollide = false

		part.Parent = workspace
		mouse.TargetFilter = part
		local x = mouse.Hit.X
		local y = mouse.Hit.Y 
		local z = mouse.Hit.Z
		part.Position = Vector3.new(math.floor(x/4) * 4, math.floor(y/4) * 4 + 2, math.floor(z/4) * 4)

	end
end)
tool.Equipped:Connect(function()
	equipped = true

	part.Transparency = 0.5

end)

tool.Unequipped:Connect(function()
	equipped = false

	part.Transparency = 1
	part.Position = Vector3.new(1,1,1)

end)
1 Like

It’s the issue actually. When you click on the left, and I’d guess bottom and front sides as well, the mouse position is halfway between one block coordinate and another.

Since you’re using flooring to turn a mouse position that is probably not on a block coordinate into the nearest whole block coordinate, you get the same behavior regardless of if the mouse pos is +0.5 blocks from a block coordinate or -0.5 blocks. floor(0.5) = 0 and floor(-0.5) = -1. In both cases the number is smaller than the original, so e.g. if you click the left side of a block, the new block is placed half a block to the right of where you clicked, which is inside the existing block! When you click the right side, the resulting block is to the right of that, which is perfect since that’s where a block should actually be placed.

The solution is to not try to place blocks exactly at the mouse pos, but instead “half a block outwards from the mouse pos”. We can get the “outwards” direction as the normal of the surface that the mouse is pointing at, and (hopefully) fix the block placement like this:

local hitSurface = mouse.TargetSurface
local hitNormal = mouse.Target.CFrame:VectorToWorldSpace(Vector3.FromNormalId(hitSurface))
local buildPos = mouse.Hit + hitNormal * 2
local x = buildPos.X
local y = buildPos.Y 
local z = buildPos.Z
part.Position = Vector3.new(math.floor(x/4) * 4, math.floor(y/4) * 4, math.floor(z/4) * 4)

the error says "Hit is not a valid member of CFrame ", when i click the oart spawns in the spawn location.

I’ve edited the script, should be fixed now

huh it seems the part placing is action strangely, but you can still place two block in one place, i think i need to somehow detect if there is a part there already or not

hmm i tried making it detect if it is on the map or not, if it is then it goes up using the surface function, but it does not work at all, heres the script:

wait()
local player = game.Players.LocalPlayer
local mouse = player:GetMouse()
local UIS = game:GetService('UserInputService')
local RS = game:GetService('ReplicatedStorage')
local tool = script.Parent
local Event = RS:WaitForChild('PlaceBlock')
local pos
equipped = false
local part = game.ReplicatedStorage.Blocks.Part:Clone()

part.Click:Destroy()
-- Surface Detection

local function Surface(surface)
	if surface.Name == "Top" then
		pos = Vector3.new(0,4,0)
	elseif surface.Name == "Bottom" then
		pos = Vector3.new(0,-4,0)
	elseif surface.Name == "Front" then
		pos = Vector3.new(0,0,-4)
	elseif surface.Name == "Back" then
		pos = Vector3.new(0,0,4)
	elseif surface.Name == "Left" then
		pos = Vector3.new(-4,0,0)
	elseif surface.Name == "Right" then
		pos = Vector3.new(4,0,0)
	end
	return pos
end




local num = 1
local blocks = RS.Blocks:GetChildren()
local Block_Choice = blocks[1]

numberOfBlocks = #blocks


script.Parent.Activated:Connect(function()
	local target = mouse.Target
	if target then
		local surface = mouse.TargetSurface
		local pos = part.Position--Surface(surface)
		Event:FireServer(target,pos,Block_Choice)
	end
end)

-- the selection part 
mouse.Move:Connect(function()
	if equipped == true then
		local target = mouse.Target
		local surface = mouse.TargetSurface
		if target == game.Workspace.Map then

		part.CanCollide = false

		part.Parent = workspace
		mouse.TargetFilter = part
		local x = mouse.Hit.X
		local y = mouse.Hit.Y 
		local z = mouse.Hit.Z
		part.Position = Vector3.new(math.floor(x/4) * 4, math.floor(y/4) * 4 + 2, math.floor(z/4) * 4)
		else
			if target ~= game.Workspace.Map then
				part.Position = Surface(surface)
				
			end
			
		end
	end
end)
tool.Equipped:Connect(function()
	equipped = true

	part.Transparency = 0.5

end)

tool.Unequipped:Connect(function()
	equipped = false

	part.Transparency = 1
	part.Position = Vector3.new(1,1,1)

end)

but it just don’t go up when i put my cursor above the block