How could I improve this button simulator game script?

Hello, I was wondering if there was a way I could improve this ButtonHandler script for my Button Simulator game. The script is parented to a folder that stores all of the buttons (in their respective currencies folders) in the workspace.

The script:

--Modules
local formatNumbersModule = require(game.ReplicatedStorage.Modules.FormatNumbers)
local boosts = require(script.Boosts)

--Variables
local Buttons = script.Parent:GetDescendants()
local debounce = false

for _, button in ipairs(Buttons) do
	if button:IsA("BasePart") then
		local currency = button.Parent.Name
		local amount = button.Amount
		local cost = button.Cost
		local requiredCurrency = button.RequiredCurrency
		local display = button.DisplayGUI.Display
		local costLabel = display.Cash
		local amountLabel = display.Amount

		costLabel.Text = formatNumbersModule.Comma(cost.Value) .. " " .. requiredCurrency.Value
		amountLabel.Text = "+" .. formatNumbersModule.Comma(amount.Value) .. " " .. currency

		button.Touched:Connect(function(hit)
			if debounce then return end
			debounce = true

			local char = hit.Parent
			local hum = char:FindFirstChild("Humanoid")
			if not hum then
				debounce = false
				return
			end

			local player = game.Players:GetPlayerFromCharacter(char)
			if player and player:FindFirstChild("PlayerStats") then
				local stats = player.PlayerStats
				local reqCurrency = stats:FindFirstChild(requiredCurrency.Value)
				local targetCurrency = stats:FindFirstChild(currency)

				if reqCurrency and targetCurrency and reqCurrency.Value >= cost.Value then
					
					-- Apply boosts if this currency has any boost relationships
					if boosts[currency] then
						for statName, multiplier in pairs(boosts[currency]) do
							local boostedStat = stats:FindFirstChild(statName)
							if boostedStat then
								local boostAmount = amount.Value * multiplier
								boostedStat.Value = boostedStat.Value + boostAmount
							end
						end
					end

					-- Main upgrade
					targetCurrency.Value = targetCurrency.Value + amount.Value
					reqCurrency.Value = reqCurrency.Value - cost.Value
				end
			end

			task.wait(1)
			debounce = false
		end)
	end
end

Thank you :smiley:

5 Likes

I would recommend you to learn ReactLua a scripting library used in many big games in Roblox, it is convenient for handling states, and bindings. I also pair it with Ripple an easy to learn package compatible with React How To: React + Roblox GitHub - littensy/ripple: 🎨 An elegant motion library for Roblox it takes abit to learn.

1 Like

AYO!? DON’T SAY SUCH BAD WORDS IN PUBLIC! No more lag propaganda. Optimization is truth; everything else is cope. Middleware? More like a CoppingWare Oh yeah, don’t forget “gold-plated” and “enterprise” nonsense. No one is buying that buzzword in big 2025. :wilted_flower:

Middleware is the root of all evil.

3 Likes

Thanks for clearing things up i really thought it makes a difference

1 Like

Your ButtonHandler script looks pretty functional.
I’d only really clean the code up a bit so it’s not so messy and indented.

Let’s start with this:

local debounce = false

for _, button in ipairs(Buttons) do
	if button:IsA("BasePart") then -- this part... i don't like it

You don’t have anything after the “end” keyword of that if statement, so you might as well write the if statement like this:

local debounce = false

for _, button in ipairs(Buttons) do
	if not button:IsA("BasePart") then continue end
    -- code; without indentation

Next up, is this part:

if debounce then return end
debounce = true

local char = hit.Parent
local hum = char:FindFirstChild("Humanoid")
if not hum then
	debounce = false
	return
end

local player = game.Players:GetPlayerFromCharacter(char)
if player and player:FindFirstChild("PlayerStats") then
    -- blah blah code
end

task.wait(1)
debounce = false

There’s no reason to set the debounce to true and then change your mind later.
Instead, I’d prefer something like this:

if debounce then return end
local player = game.Players:GetPlayerFromCharacter(hit.Parent)
if not player or not player:FindFirstChild("PlayerStats") then return end
debounce = true
-- blah blah code
task.wait(1)
debounce = false

See, this is a bit more compact, no?

Besides, why would the button still be on debounce if there is no player?
You did solve that by checking if there is a humanoid in the character, but using my way you don’t have to check for a character or a humanoid, because the player variable is all we need.

Oh and there’s one more piece of code that can be a tiny bit better:

local boostedStat = stats:FindFirstChild(statName)
if boostedStat then
	local boostAmount = amount.Value * multiplier
	boostedStat.Value = boostedStat.Value + boostAmount
end

There’s no reason to define a “boostAmount” variable if you’re not going to use it more than once.
Also… Did you know there’s this feature called " += "?

local boostedStat = stats:FindFirstChild(statName)
if boostedStat then boostedStat.Value += amount.Value * multiplier end

Boom! Only 2 lines of code!

Conclusion

I hate indentation, please hate it too. :heart_eyes:

1 Like

I disagree, wrapping a calculation inside a variable makes the code more readable for a developer who wants to understand what the calculation is used for. Btw, variables have multiple use cases that aren’t just “storing a value that is used/modified many times”.

That’s a fair argument, though I think in this specific context it’s not as big of a deal since the variable is just a simple multiplication, but in a more general sense you’d be right.

Btw, variables have multiple use cases that aren’t just “storing a value that is used/modified many times”

Can you explain what those uses are?
I’m not trying to sound rude, but isn’t the definition of a variable “a stored value” ?
I know one of the other use cases is making the code more understandable, but since you said there’s more use cases after saying “it makes it more readable” I figured there are more?

1 Like

Sorry, it’s just that I thought there were more use cases than those two.

1 Like