Hi all! I’m currently developing a tycoon game, but I’ve run into a slight issue: when a player touches a button, the script is supposed to spawn an object, update the players’ income, and disappear. The spawn works great and the objects do not duplicate, but for some odd reason the game will occasionally recognize multiple button presses in the literal millisecond it takes for the button to disappear, resulting in the player’s income value being increased by up to four times the button’s intended value.
Button.Touched:Connect(function(hit)
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if not player then return end
if plot:GetAttribute('Owner') ~= player.UserId then return end
local itemToUnlockId = Button:GetAttribute('UnlockId')
if not itemToUnlockId then warn('You forgot to add an UnlockId attribute!') return
end
-- Check if player has enough money
local Price = Button:GetAttribute('Price')
if Price then
if player.leaderstats.Cash.Value < Price then
warn('You cannot afford this item!')
return
end
player.leaderstats.Cash.Value -= Price
end
for _, item in TempItems:GetChildren() do
if item:GetAttribute('Id') ~= itemToUnlockId then continue end
local itemClone = item:Clone()
local itemCFrame
if itemClone:IsA('Model') then
itemCFrame = itemClone:GetPivot() --Model
elseif itemClone:IsA('BasePart') then
itemCFrame = itemClone.CFrame --BasePlate (Part, Union, Mesh, etc.)
end
local relativeItemCFrame = TempPlot.CFrame:ToObjectSpace(itemCFrame)
local worldCFrameOfNewPlot = plot.CFrame:ToWorldSpace(relativeItemCFrame)
if itemClone:IsA('Model') then
itemClone:PivotTo(worldCFrameOfNewPlot)
elseif itemClone:IsA('BasePart') then
itemClone.CFrame = worldCFrameOfNewPlot
end
-- Updates the player's income to Revenue attribute in button
local Income = player.leaderstats.Income
local Revenue = Button:GetAttribute('Revenue')
Income.Value += Revenue
itemClone.Parent = itemsFolder
Button:Destroy()
print('Button has been pressed by '..player.Name..' and their income has been increased to '..Income.Value..'!')
end
end)
Rather than rely on the destruction of the button to disable repeated interaction, formally disconnect your callback from the Touched event:
local function onLivingPlayerTouched(part: BasePart, callback: (Player) -> ()): RBXScriptConnection
return part.Touched:Connect(function(otherPart: BasePart)
local character = otherPart.Parent :: Model
local humanoid = character:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health == 0 then
return
end
local player = Players:GetPlayerFromCharacter(character)
if player then
callback(player)
end
end)
end
local connection
connection = onLivingPlayerTouched(Button, function(player: Player)
connection:Disconnect()
-- Reward player.
end)
Just tried adding this to the script, and it seems to have had no change on the outcome. I put the local function close to the beginning of my script and the local connection right before the reward- is there a particular order these need to be introduced into the script in order for the formal disconnect to properly execute?
Hey! Is there a reason why your income logic is inside of the for loop? This could be causing the issue as each time the loop occurs income would increase. Let me know if removing the income logic from the for loop did fix the issue. You can change the :Connect to :Once and disconnect the connection but you are already destroying the part itself so these would not do anything. I am 99% sure its because your income logic is inside of the for loop.
By making it a :Once() system instead of a :Connect() system, it would make the buttons vulnerable to being pressed when a player who doesn’t own the plot or have enough cash walks over it. If that happens, the button can no longer be purchased and the game will not be able to move forward.
I tried relocating the income logic to a new location outside of the for loop, but that made no change to the outcome. The button is still vulnerable to being pressed multiple times before disappearing.
I added a debounce to the events, but it seems that it’s only had an effect on the objects- the buttons can now only spawn a single object rather than being able to spawn multiple simultaneously. The income issue is still occurring.
Add an if statement and a boolean to check if the player already bought the button. Please let me know if this helps!
Code
local Bought = false
local Running = false -- You may not need this, just in case.
Button.Touched:Connect(function(hit)
if Bought or Running then return end
Running = true
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if not player then return end
if plot:GetAttribute('Owner') ~= player.UserId then return end
local itemToUnlockId = Button:GetAttribute('UnlockId')
if not itemToUnlockId then warn('You forgot to add an UnlockId attribute!') return
end
-- Check if player has enough money
local Price = Button:GetAttribute('Price')
if Price then
if player.leaderstats.Cash.Value < Price then
warn('You cannot afford this item!')
return
end
player.leaderstats.Cash.Value -= Price
Bought = true
end
for _, item in TempItems:GetChildren() do
if item:GetAttribute('Id') ~= itemToUnlockId then continue end
local itemClone = item:Clone()
local itemCFrame
if itemClone:IsA('Model') then
itemCFrame = itemClone:GetPivot() --Model
elseif itemClone:IsA('BasePart') then
itemCFrame = itemClone.CFrame --BasePlate (Part, Union, Mesh, etc.)
end
local relativeItemCFrame = TempPlot.CFrame:ToObjectSpace(itemCFrame)
local worldCFrameOfNewPlot = plot.CFrame:ToWorldSpace(relativeItemCFrame)
if itemClone:IsA('Model') then
itemClone:PivotTo(worldCFrameOfNewPlot)
elseif itemClone:IsA('BasePart') then
itemClone.CFrame = worldCFrameOfNewPlot
end
-- Updates the player's income to Revenue attribute in button
local Income = player.leaderstats.Income
local Revenue = Button:GetAttribute('Revenue')
Income.Value += Revenue
itemClone.Parent = itemsFolder
Button:Destroy()
print('Button has been pressed by '..player.Name..' and their income has been increased to '..Income.Value..'!')
Running = false
end
end)