Hello, I am trying to create a new tycoon game sorta following AlvinBlox’s tutorial from back in 2024 (I’m using some of his knowledge and applying it to my own scripting, so some of it is similar but some of it is not) and I have a separate script to handle buttons.
However, even when I destroy the button, it runs the event multiple times and the purchased items will get duplicated several times. I’ve tried multiple methods to add a “debounce” to prevent the event from running more than once, but none of it seemed to work. Any suggestions are appreciated!
Code from my button handler:
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local tycoons = game.Workspace.Tycoons
local templateTycoon = tycoons.TemplateMiner
local looping = true
while looping do
for i, tycoon in tycoons:GetChildren() do
local buttonFolder = tycoon.Buttons
for i, button in buttonFolder:GetChildren() do
button.PrimaryPart.Touched:Connect(function(part)
local player = Players:GetPlayerFromCharacter(part.Parent)
if not player then return end
if tycoon.Owner.Value ~= player then return end
-- TODO: Buying functions
local unlockID = button:GetAttribute("UnlockID")
for i, object in templateTycoon.Purchases:GetChildren() do
if object:GetAttribute("ID") ~= unlockID then continue end
local purchase = object:Clone()
local objectCFrame
if purchase:IsA("Model") then
objectCFrame = purchase:GetPivot()
elseif purchase:IsA("BasePart") then
objectCFrame = purchase.CFrame
end
local relativeCFrame = templateTycoon.Entrance.CFrame:ToObjectSpace(objectCFrame)
local tycoonWorldCFrame = tycoon.Entrance.CFrame:ToWorldSpace(relativeCFrame)
if purchase:IsA("Model") then
purchase:PivotTo(tycoonWorldCFrame)
elseif purchase:IsA("BasePart") then
purchase.CFrame = tycoonWorldCFrame
end
purchase.Parent = tycoon.Purchases
button:Destroy()
end
end)
end
end
task.wait(1)
end
Okay I added a simple debounce system. Sorry if it’s a little sloppy I did this on my phone.
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local tycoons = game.Workspace.Tycoons
local templateTycoon = tycoons.TemplateMiner
local looping = true
local Debounces = {}
while looping do
for i, tycoon in tycoons:GetChildren() do
local buttonFolder = tycoon.Buttons
for i, button in buttonFolder:GetChildren() do
button.PrimaryPart.Touched:Connect(function(part)
local player = Players:GetPlayerFromCharacter(part.Parent)
if Debounces[player.UserId] then return end
Debounces[player.UserId] = true
if not player then return end
if tycoon.Owner.Value ~= player then return end
-- TODO: Buying functions
local unlockID = button:GetAttribute("UnlockID")
for i, object in templateTycoon.Purchases:GetChildren() do
if object:GetAttribute("ID") ~= unlockID then continue end
local purchase = object:Clone()
local objectCFrame
if purchase:IsA("Model") then
objectCFrame = purchase:GetPivot()
elseif purchase:IsA("BasePart") then
objectCFrame = purchase.CFrame
end
local relativeCFrame = templateTycoon.Entrance.CFrame:ToObjectSpace(objectCFrame)
local tycoonWorldCFrame = tycoon.Entrance.CFrame:ToWorldSpace(relativeCFrame)
if purchase:IsA("Model") then
purchase:PivotTo(tycoonWorldCFrame)
elseif purchase:IsA("BasePart") then
purchase.CFrame = tycoonWorldCFrame
end
purchase.Parent = tycoon.Purchases
button:Destroy()
task.delay(1, function()
Debounces[player.UserId] = nil
end)
end
end)
end
end
task.wait(1)
end
Seems that you set new Touched handler each second.
Do you really need that loop?..
Sorry if I missed something but seems that debounce logic here fixes consequences but not the cause.
As af_2048 mentions, a debounce is likely not going to be helpful here. Sure, if the computation necessary to decide whether the button should be destroyed took a long time then it’s possible the button’s touched event would get triggered more then one time before the button got destroyed. If that’s the case, a debounce would help with that.
The main issue here however is very clearly the fact that all buttons for all tycoons are being given a touched connection once a second. You only need 1 touched connection that will last the entirety of the game, so just remove the while loop.
Ah I see. I tried that before and the other problem I had was that when new buttons are added, no event is assigned to them. This was my lazy solution to that issue but it seems it was not a very good solution at all. Do you have any recommendations for how I’d be able to account for new buttons being added during a test?
EDIT:
I have split up the button function into a separate function that is now called by a couple connect events. One of them is the same one I have here, just only checking once the game starts to activate any buttons that might be there. The second one is detecting for buttons being added to the button folders, and then activating those buttons as well. I have tested it and it works!
Thanks for this clarification, now I see why you need the loop.
My 5 cents here is that most likely you know the place you add such buttons so it looks reasonable to initialize Touched handler there once.
It helps for me to have this, cause the way my tycoons are set up is that it pulls from a single template and just adds everything over time as you buy. That way I don’t have to copy and paste the same tycoon 4-6 times when I make a small change, it’ll just be automatically applied to all of the tycoons.