A way I can get the GUI button a player clicked without scripting each of them?

It’s time consuming scripting each button at a time and lengthens your script, and I’m wondering if there’s a way I can get the button the player clicked without defining each of them as a variable? (I also use typescript)

1 Like

If you don’t define them, how would a script know which button is being pressed?

What you could do is have a single script for each button, but if you are looking to move your GUIs around it is better to have most or all of your GUI objects scripted in one place (ideally). Regardless, if you do this then you end up referencing the button one way or another.

I was hoping UserInputService might have some event for what you are looking for, but alas it does not (from what I know of).

You can already do this if you just reference a path to the button, like this

script.Parent.MouseButton1Click:Connect(function()

end)

That way you avoid locals in general. If the buttons are descendants then you might need to wait until they are loaded, much like you would in declaring them as variables.

1 Like

This only works if all the TextButtons are children of a single parent, not multiple.
What you can do is loop through the children of their parent, and use a

if Instance:IsA('TextButton') then

line to filter through all of the GuiObjects. You will know which Button is which by doing an if statement like

if Instance.Name == 'OpenFrameButton' then

I use this method all the time because I too hate defining each TextButton when there’s many to define.

1 Like

Alright, thank you for taking your time to respond. Appreciate it :slight_smile:

If I understood your question properly, I think you mean something like this

local Plr = game:GetService("Players").LocalPlayer;
local UIS = game:GetService("UserInputService");
local PlrUI = Plr.PlayerGui;

UIS.InputBegan:Connect(function(input, processed)
	if (input.UserInputType == Enum.UserInputType.MouseButton1 and processed) then
		local GuiElements = PlrGUI:GetGuiObjectsAtPosition(input.Position.X, input.Position.Y);
		for _,g in ipairs(GuiElements) do
			if g:IsA("TextButton") or g:IsA("ImageButton") then
				--<Do thingys 
			end
		end
	end
end)

will work for any device I think, and also works on buttons added late VIA scripts

3 Likes

Something that I use a lot is iterate over each button in a GuiElement and use a function to bind them. There’s no need to use UIS for this

local function BindButtonToFunction(btn, func)
  btn.MouseButton1Click:Connect(func)
end

for _, v in pairs(GuiElement:GetChildren()) do
  if v:IsA("TextButton") then
    BindButtonToFuncton(btn, function()
       print(btn.Name)
    end)
  end
end
7 Likes

If I used UIS, I’d have to enter the coordinates for each button, plus there are multiple pages included in the gui which would require me to do more. @FilteredDev’s solution sounds the best, binding them using a loop is much simpler and effective. Thanks for taking your time to respond.

What? you don’t need to enter each coordinate. input.Position.X returns the position of where the input began. So since I used UserInputType.MouseButton, It’ll return the mouses position. Equivalent to using Mouse.X, Mouse.Y.

I wasn’t sure if it would work for UI elements which are using scale. And does it only apply to elements which are visible, or all of them, regardless of them being visible or not? Because I have multiple UIs overlaying each other.

It’ll apply to any TextButton or ImageButton

You can check if it’s visible by saying

UIS.InputBegan:Connect(function(input, processed)
	if (input.UserInputType == Enum.UserInputType.MouseButton1 and processed) then
		local GuiElements = PlrGUI:GetGuiObjectsAtPosition(input.Position.X, input.Position.Y);
		for _,g in ipairs(GuiElements) do
			if g:IsA("TextButton") or g:IsA("ImageButton") then
				if(g.Visible == true) then
        --< I usually use an ignore list
                end;
			end
		end
	end
end)

Alrighty, thank you for helping, I’ll give it a try

I have an event based approach for doing this if I understand this correctly.

--On a module script
local player = game:GetService("Players").Local player
local module = {OnGlobalClick = {}}

function module.OnGlobalClick:Connect(func)
    for _, gui in pairs(player.PlayerGui:GetDescendants()) do
        if gui:IsA("TextButton") or gui:IsA("ImageButton") then 
            gui.MouseButton1Click:Connect(function()
                func(gui)
            end)
        end
    end
    player.PlayerGui.DescendantAdded:Connect(function(desc)
        if desc:IsA("TextButton") or desc:IsA("ImageButton") then
            desc.MouseButton1Click:Connect(function()
                func(desc)
            end)
        end
    end)
end

return module

Then you could have a local script

local module = require(theModule)

module.OnGlobalClick:Connect(function(buttonThatWasClicked)
    print(buttonThatWasClicked.Name.." was clicked")
end)

This will add a new connection to all present clickable guis aswell as newly added ones. This can be used in any local script that is able to access the module.

Note: This code can be optimized in the way you want
This might not be very convenient for a single operation such as this, but when you have multiple utilities like this, I would prefer a module.

1 Like