More effective approach to assign functions to buttons in a menu

I am sure you’ve encountered the same thing when we have multiple GUI buttons in a menu, it is ineffective to assign a function for each individual button, so most of us use a numeric loop / or in pairs (haven’t tried that yet) to get all the buttons. Then we assign a function to this anonymous index.

Like this:

for i = 1, #buttons do
	if buttons[i]:IsA("TextButton") then
		buttons[i].MouseButton1Down:Connect(function()
			--code
		end)
	end
end

It certainly doesn’t look that appealing to me and I am wondering if any experienced scripters have any input on this subject.

4 Likes

If the buttons all do the same, you can just do something like this:

local function sameFunction()
  print("Hi")
end

for i = 1, #buttons do
  if buttons[i]:IsA("TextButton") then
    buttons[i].MouseButton1Down:Connect(sameFunction)
  end
end

If they are slightly different, like selecting a vehicle to spawn, I sometimes use this:

local function selectFunction(name)
  print("pressed"..name:GetFullName())
end

for i = 1, #buttons do
  if buttons[i]:IsA("TextButton") then
    buttons[i].MouseButton1Down:Connect(function()
      selectFunction(buttons[i])
    end)
  end
end

I’m not sure if that’s what you meant though.

3 Likes

Most of my buttons are created at runtime to allow special hover effects to be done on them, so I have a function like this, probably in a module:

function createButton( options, callback )
    local button = Instance.new( 'TextButton' )
    -- create the button here, set dimensions, text, etc.

    -- Hover effects
    button.MouseEnter:Connect( ... )
    button.MouseLeave:Connect( ... )

    -- Click handler
    button.Activated:Connect( callback )
    return button
end

And then in my GUI initialisation as part of my main script a whole chunk of it is just creating the buttons.

createButton( {
    text = 'My Button',
    dimensions = {
        x_scale = 0.4,
        x_offset = 10,
        -- you get the idea
    },
    position = {...},
    parent = theGuiFrame,
}, yourFunc1 )

createButton( {
    text = 'Another Button',
    dimensions = {...},
    position = {...},
    parent = theGuiFrame,
}, yourFunc2 )

There’s no completely clean way to do it, but splitting common code into modules and calling it when you need it is one way to make it easier to read.


If you want to stick with already-created buttons

Your loop is fine if you don’t have any special effects and your buttons already exist. You could put a StringValue inside of each button called “Callback” with the name of the function you’d like to run instead:

local yourFunctions = {
    [ 'func1' ] = function ()
        print( 'hello world' )
    end,
    [ 'func2' ] = function ()
        theGui.Enabled = false
    end,
}

for _, d in pairs( theGui:GetDescendants() ) do
    if d:IsA( 'GuiButton' ) and d:FindFirstChild( 'Callback' ) then
        d.Activated:Connect( function ()
            yourFunctions[ d.Callback.Value ]()
        end )
    end
end

But all your functions will need to be in a table as shown if you go for this method.

4 Likes

That is the same code example I provided. And wanted to improve from.

2 Likes

It is not. Yours is quite a bit less efficient due to generating 1 anonymous function per button. And I gave 2 examples.

3 Likes

From my experience, there is not much you could improve from with the information the prior two posters given to you.

It may not seem like your code looks good, but it is one of the most efficient ways to program multiple instances without using a ludicrous amount of if statements and/or functions. Both the prior two approaches should be sufficient.

Whenever this comes up when I do UI coding I just make a single button inside my LocalScript with another LocalScript to connect the click function inside of the button and then copy it, or more recently just unknowingly use your method, and I think that your method is a lot better but that was old code.

I don’t think it’s that unappealing to me. I don’t think there’s any other way to go about this, don’t stress over it too much, I do this sometimes and wonder if this is the absolutely right way to do this but at the end of the day no ordinary player is (hopefully not) checking your code to see if it’s perfect.

Also I for this kinda stuff I always use in pairs loops instead of numeric loops. But it’s only a slight difference and shouldn’t affect anything.

in a specific menu, if all the buttons have the same hover function, ill assign that in a loop, and then assign individual click functions separately
for one place, i had a special effect for buttons where clicking it would make a circle appear, expand, and slowly disappear (like a google button) for this, i tagged buttons with a tag and connected to the events to all the tagged objects