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.
Build mode:
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!