Grid placing system

I mean something like BedWars or SkyWars where when you look in front of you, you can see a transparent block.

I made this for my game.

It’s a bit buggy but it does the job done.

Place a Local Script in a Tool.

Place a Normal script in the tool.

Place a Folder named “PartsFolder” in the workspace and place all the parts you want in the map.

Place a Folder named “PartsPreview” in the workspace for the prediction block.

Place a Remote event named “PlaceBlock” in the tool

This is the code for the Local Script:

local PlaceBlockEvent = script.Parent.PlaceBlock

local equipped = false
local uis = game:GetService("UserInputService")
local mouse = game.Players.LocalPlayer:GetMouse()
local character = game.Players.LocalPlayer.Character or game.Players.LocalPlayer.CharacterAdded:Wait()
local cam = workspace.CurrentCamera


local function findClosestPart(preview)
	local lowestMagParts = {}
	local currentPart = nil
	local currentClosestDir = nil
	local currentClsoestmag = nil 
	for _,v in pairs(game.Workspace.PartsFolder:GetDescendants()) do
		if v:IsA("BasePart") then
			local magnitude1 = (character.PrimaryPart.Position - v.Position).Magnitude
			if magnitude1 <= 25 then
				table.insert(lowestMagParts, v)
			end
		end
	end
	local lowestMagnitude = nil
	for _,v in pairs(lowestMagParts) do
		if v:IsA("BasePart") then
			local mouse3DPos = character.PrimaryPart.Position + (mouse.Hit.Position - character.PrimaryPart.Position).Unit * (character.PrimaryPart.Position - v.Position).Magnitude
			local mag = (v.Position - mouse3DPos).Magnitude
			if not lowestMagnitude then
				lowestMagnitude = mag
				currentPart = v
				continue
			end
			if mag < lowestMagnitude then
				lowestMagnitude = mag
				currentPart = v
			end
		end
	end
	
		local surfaceIDs = {
			top = currentPart.Position + Vector3.new(0,25,0),
			bottom = currentPart.Position - Vector3.new(0,25,0),
			left = currentPart.Position - Vector3.new(25,0,0),
			right = currentPart.Position + Vector3.new(25,0,0),
			front = currentPart.Position - Vector3.new(0,0,25),
			back = currentPart.Position + Vector3.new(0,0,25),
		}



		for surfaceId, pos in pairs(surfaceIDs) do
			local mag = ((character.PrimaryPart.Position + (mouse.Hit.Position - character.PrimaryPart.Position).Unit * 25) - pos).Magnitude
			if not currentClsoestmag then
				currentClsoestmag = mag
				currentClosestDir = surfaceId
				continue
			end
			if mag < currentClsoestmag then
				currentClsoestmag = mag
				currentClosestDir = surfaceId
			end
		end
		if currentClosestDir ~= nil then
			table.clear(lowestMagParts)
			return currentPart, currentClosestDir
		end

end

local connection = nil

script.Parent.Equipped:Connect(function()
	if equipped == false then
		equipped = true
		local clonePreview = Instance.new("Part")
		clonePreview.Parent = game.Workspace:WaitForChild("PartsPreview")
		clonePreview.Name = script.Parent.Parent.Name.."Preview"
		clonePreview.Transparency = 1
		clonePreview.Size = Vector3.new(3,3,3)
		clonePreview.CanCollide = false
		clonePreview.Anchored = true
		local selection = Instance.new("SelectionBox", clonePreview)
		selection.Visible = true
		selection.Adornee = clonePreview
		selection.LineThickness = 0.05
		selection.Color3 = Color3.fromRGB(0,0,0)
		for _,v in pairs(game.Workspace.PartsPreview:GetChildren()) do
			if v ~= clonePreview then
				v:Destroy()
			end
		end
		mouse.TargetFilter = clonePreview
		task.defer(function()
			while wait() do
				if equipped == true then
					local mouseX,mouseY,mouseZ = mouse.Hit.X, mouse.Hit.Y, mouse.Hit.Z
					local mouseTarget = mouse.TargetSurface
					if mouse.Target ~= nil then
						if mouseTarget == Enum.NormalId.Top then
							clonePreview.CFrame = (CFrame.new(mouse.Target.Position + Vector3.new(0,3, 0))) --- the 3 is the size of the block
						elseif mouseTarget == Enum.NormalId.Bottom then
							clonePreview.CFrame = (CFrame.new(mouse.Target.Position - Vector3.new(0,3, 0))) --- the 3 is the size of the block
						elseif mouseTarget == Enum.NormalId.Left then
							clonePreview.CFrame = (CFrame.new(mouse.Target.Position - Vector3.new(3, 0,0))) --- the 3 is the size of the block
						elseif mouseTarget == Enum.NormalId.Right then
							clonePreview.CFrame = (CFrame.new(mouse.Target.Position + Vector3.new(3,0, 0))) --- the 3 is the size of the block
						elseif mouseTarget == Enum.NormalId.Front then
							clonePreview.CFrame = (CFrame.new(mouse.Target.Position - Vector3.new(0,0, 3))) --- the 3 is the size of the block
						elseif mouseTarget == Enum.NormalId.Back then
							clonePreview.CFrame = (CFrame.new(mouse.Target.Position + Vector3.new(0,0, 3))) --- the 3 is the size of the block
						end
					elseif mouse.Target == nil then
						local closestPart, direction = findClosestPart(clonePreview)
						if tostring(direction) == "top" then
							clonePreview.CFrame = (CFrame.new(closestPart.Position + Vector3.new(0,3, 0))) --- the 3 is the size of the block
						elseif tostring(direction) == "bottom" then
							clonePreview.CFrame = (CFrame.new(closestPart.Position - Vector3.new(0,3, 0))) --- the 3 is the size of the block
						elseif tostring(direction) == "left" then
							clonePreview.CFrame = (CFrame.new(closestPart.Position - Vector3.new(3,0,0))) --- the 3 is the size of the block
						elseif tostring(direction) == "right" then
							clonePreview.CFrame = (CFrame.new(closestPart.Position + Vector3.new(3,0, 0))) --- the 3 is the size of the block
						elseif tostring(direction) == "front" then
							clonePreview.CFrame = (CFrame.new(closestPart.Position - Vector3.new(0,0, 3))) --- the 3 is the size of the block
						elseif tostring(direction) == "back" then
							clonePreview.CFrame = (CFrame.new(closestPart.Position + Vector3.new(0,0, 3))) --- the 3 is the size of the block
						end
						closestPart = nil
						direction = nil
					end	
				end
			end
		end)

		connection = mouse.Button1Down:Connect(function()
			if equipped == true then
				for _,v in pairs(game.Workspace:WaitForChild("PartsFolder"):GetDescendants()) do
					if v:IsA("BasePart") then
						if clonePreview.Position == v.Position then
							return
						end
					end
				end

				local primaryPart = clonePreview.CFrame
				PlaceBlockEvent:FireServer(primaryPart)
			end

		end)
	end
end)

script.Parent.Unequipped:Connect(function()
	if equipped == true then
		equipped = false
		if workspace.PartsPreview:FindFirstChild(script.Parent.Parent.Name.."Preview") then
			local preview = workspace.PartsPreview:FindFirstChild(script.Parent.Parent.Name.."Preview")
			preview:Destroy()
		end
	end
	connection:Disconnect()
	connection = nil
end)
 

Here is the code for the Normal script:

local event = script.Parent.PlaceBlock
local part = game.ReplicatedStorage:WaitForChild("Wood Plank") ---- name of the part in the replicated storage you want to clone for the block placement system

event.OnServerEvent:Connect(function(plr, pos)
	local Clone = part:Clone()
	Clone.Parent = game.Workspace:WaitForChild("PartsFolder")
	Clone.CFrame = pos
end)

This is my first time making a reply so let me know if you have problems!

2 Likes

Also here’s a video clip of the tool. It’s a bit lag since I’m on a laptop

robloxapp-20220302-2138143.wmv (3.4 MB)

Thank you so much for your help! I’ve been looking around for something like this.

However, it does not seem to work. There is currently nothing in the output.

I’ve followed your directions and re-checked the script…

EDIT: Fixed the problem, i didnt have a handle lol

However, for some odd reason, I get this error.
image

Here’s where it’s causing the issue.

It seems to be happening when I take the mouse off the map.

Thanks anyways, you are a lifesaver!

1 Like

Add this if so that the error will go away.

    if currentPart ~= nil then
        local surfaceIDs = {
			top = currentPart.Position + Vector3.new(0,25,0),
			bottom = currentPart.Position - Vector3.new(0,25,0),
			left = currentPart.Position - Vector3.new(25,0,0),
			right = currentPart.Position + Vector3.new(25,0,0),
			front = currentPart.Position - Vector3.new(0,0,25),
			back = currentPart.Position + Vector3.new(0,0,25),
		}



		for surfaceId, pos in pairs(surfaceIDs) do
			local mag = ((character.PrimaryPart.Position + (mouse.Hit.Position - character.PrimaryPart.Position).Unit * 25) - pos).Magnitude
			if not currentClsoestmag then
				currentClsoestmag = mag
				currentClosestDir = surfaceId
				continue
			end
			if mag < currentClsoestmag then
				currentClsoestmag = mag
				currentClosestDir = surfaceId
			end
		end
		if currentClosestDir ~= nil then
			table.clear(lowestMagParts)
			return currentPart, currentClosestDir
		end
    end

Alright! I’ll see if it works once I get back.

i have a problem with the first line

of the local script

Blockquote

@XObbyCreatorX the preview is not destroying

Are there any errors? If you’re talking about this line:
1 | local PlaceBlockEvent = script.Parent.PlaceBlock
A remote event is needed inside the tool.

i do that but its not working its just the preview that does not destroy

I will work on a solution. Thanks for informing me!

ok thx bruh this thing it saying i need characters

hey did you find a solution for the fix

Hey nvm i found the problem on the script it was so easy