Hey, so I’m working on a block placement game, similar to Minecraft. There is a build mode and a delete mode. Both are toggled via TextButtons. When build mode is enabled, it works flawlessly. Blocks are placed as they should be. Then if you switch to delete mode, build mode stops functioning (this is intended behavior, as you can’t be building and deleting blocks at the same time) Delete mode removes the blocks you tap on. The issue comes once you switch back to build mode after using delete mode. It now deletes and places blocks simultaneously. I asked ChatGpt about this issue and it said I need to disconnect my connections. It then provided me with a revised version of my code implementing this. The code it provided me has the same issue as I was having.
I do however believe it is correct about the connections being responsible for this bug. It just failed to properly implement it. I will post my code, with chatGpt’s revision down below. I do not know how connections work. If anyone could take a look at my code, that would be amazing. The code will probably make you cringe as I’m a beginner scripter. I’m sure there is a lot that isn’t efficient and very messy. I’m open to constructive criticism on how to improve the rest of my code. But my primary goal in this post is to fix this bug.
The connections are at the bottom of the script, but the rest of the script may also be a part of the issue.
Thank you so much to anyone who actually looks through this script, I know it long and I appreciate you taking the time to help me
Local Script (This is the script that the issue is likely coming from)
-- // Declarations local LocalPlayer = game.Players.LocalPlayer local replicatedStorage = game:GetService("ReplicatedStorage") local mouse = LocalPlayer:GetMouse() local selectorGui = script.Parent.Parent.Parent.BlockSelector local block = LocalPlayer.PlayerGui:WaitForChild("Block") local enabled = true local currentBlock = nil -- To keep track of the current block -- // Delete Declarations local deleteButton = script.Parent.Parent.Delete local deleteMode = false deleteBounce = false local deleteEvent = replicatedStorage:WaitForChild("Remotes"):WaitForChild("DeleteEvent") local function showSelectorGui() selectorGui.Enabled = true deleteMode = false end script.Parent.MouseButton1Click:Connect(showSelectorGui) local function updateBlockPreview() print(deleteMode) -- // Prevents player from placing blocks from the other side of the map if (LocalPlayer.Character.HumanoidRootPart.Position - mouse.Hit.p).Magnitude > 20 or deleteMode == true then if currentBlock then currentBlock:Destroy() end return end -- // Ensures there is only one update block at a time, not thousands if currentBlock then currentBlock:Destroy() -- Destroy the current block if it exists currentBlock = nil -- Reset the current block end -- // Create a previewBlock local blockTemplate = replicatedStorage.Blocks:FindFirstChild(block.Value) if enabled and blockTemplate then local previewBlock = blockTemplate:Clone() previewBlock.CanCollide = false previewBlock.Transparency = 0.5 local descendants = previewBlock:GetDescendants() -- // Make the previewBlock partially transparent for i, v in pairs(descendants) do if v:IsA("Decal") then v.Transparency = 0.5 end end mouse.TargetFilter = previewBlock previewBlock.Parent = workspace.PreviewBlocks -- // Place it accurately on a grid local offset = Vector3.new(0, 0, 0) if mouse.TargetSurface == Enum.NormalId.Back then offset = Vector3.new(0, 0, 3) elseif mouse.TargetSurface == Enum.NormalId.Front then offset = Vector3.new(0, 0, -3) elseif mouse.TargetSurface == Enum.NormalId.Top then offset = Vector3.new(0, 3, 0) elseif mouse.TargetSurface == Enum.NormalId.Bottom then offset = Vector3.new(0, -3, 0) elseif mouse.TargetSurface == Enum.NormalId.Right then offset = Vector3.new(3, 0, 0) elseif mouse.TargetSurface == Enum.NormalId.Left then offset = Vector3.new(-3, 0, 0) end previewBlock.Position = mouse.Target.Position + offset currentBlock = previewBlock -- Set the current block to the newly created one end end mouse.Move:Connect(updateBlockPreview) -- // This function places a block on the server, which all players can see. local function placeBlock() if enabled and mouse.Target and mouse.Target.Name then if (LocalPlayer.Character.HumanoidRootPart.Position - mouse.Hit.p).Magnitude > 20 or deleteMode == true then return end -- // Fire the remote event replicatedStorage.Remotes.PlaceEvent:FireServer(mouse.Target, mouse.TargetSurface, block.Value) else print('Conditions not met') end end mouse.Button1Down:Connect(placeBlock) local deleteEventConnection = nil -- Add this line to keep track of the connection local function delete() deleteMode = true -- Toggle deleteMode if deleteMode == true then -- Connect the event only when deleteMode is true deleteEventConnection = mouse.Button1Down:Connect(function() if mouse.Target and mouse.Target.Name and mouse.Target.Parent == workspace.Blocks and deleteBounce == false then deleteBounce = true deleteEvent:FireServer(mouse.Target) wait(0.1) deleteBounce = false end end) else -- Disconnect the event when deleteMode is false if deleteEventConnection then deleteEventConnection:Disconnect() end end end deleteButton.MouseButton1Click:Connect(delete)
Server Script: (I don’t think this script is very relevant to the issue)
local replicatedStorage = game:GetService("ReplicatedStorage") local placeEvent = replicatedStorage:WaitForChild("Remotes"):WaitForChild("PlaceEvent") local deleteEvent = replicatedStorage:WaitForChild("Remotes"):WaitForChild("DeleteEvent") -- // Place a block placeEvent.OnServerEvent:Connect(function(player, target, target_surface, block_type) local blockTemplate = replicatedStorage.Blocks:FindFirstChild(block_type) if blockTemplate then local newBlock = blockTemplate:Clone() newBlock.Parent = workspace.Blocks local offset = Vector3.new(0, 0, 0) if target_surface == Enum.NormalId.Back then offset = Vector3.new(0, 0, 3) elseif target_surface == Enum.NormalId.Front then offset = Vector3.new(0, 0, -3) elseif target_surface == Enum.NormalId.Top then offset = Vector3.new(0, 3, 0) elseif target_surface == Enum.NormalId.Bottom then offset = Vector3.new(0, -3, 0) elseif target_surface == Enum.NormalId.Right then offset = Vector3.new(3, 0, 0) elseif target_surface == Enum.NormalId.Left then offset = Vector3.new(-3, 0, 0) end newBlock.Position = target.Position + offset else print("Block template not found for type: " .. block_type) end end) -- // Delete the target block deleteEvent.OnServerEvent:Connect(function(player, target) target:Destroy() end)
I decided to use a Christian cross to show the actual issue itself since it’s a simple build.
Delete Mode: (deleted the bottom block)
Switching back to build mode after deleting: (I clicked on the far-left block. It deleted the block that was originally there and placed a new one in front of it, this is the primary bug)
Feel free to ask any questions about the scripts and or how the placement system works.
Any feedback/suggestions are much appreciated, thanks!