I’m trying to create a GUI to allow player interaction with a modular system I’ve made for testing and implementation. Each component has its own frame that can be dragged around, and inside those frames are buttons for manipulation. The main frame everything is placed in as well can be clicked and dragged to scroll.
Because the interactions with the frames are controlled with InputBegan, I keep encountering an issue where if I click on any element, the InputBegan connection fires that element and all of its ancestors.
The obvious answer would be to put a debounce in every connection preventing its ancestors from being interacted with, but this would require a lot of work and maintenance, and would require adding connection to GUI elements that otherwise wouldn’t need them, like cosmetic text labels.
Is there anyway to check if the mouse is over a descendant when InputBegan is triggered?
That list seems to be ordered top-to-bottom luckily but I can’t confirm this.
So check if the first one is a clickable one (maintain this set yourself like a “clicked” event) and either call the handler, or do nothing.
Seems to work:
local player = game.Players.LocalPlayer
local playerGui = player.PlayerGui
local UserInputService = game:GetService("UserInputService")
-- set of GUIs that can be clicked on (if not blocked)
local clickable = {
[script.Parent.Frame] = true;
-- script.Parent.Frame.Frame intentionally left out
[script.Parent.Frame2] = true;
[script.Parent.Frame2.Frame] = true;
}
-- make everything that's not clickable black so we know
for _, gui in pairs(script.Parent:GetDescendants()) do
if gui:IsA("GuiObject") and not clickable[gui] then gui.BackgroundColor3 = Color3.new() end
end
local function OnClicked(gui, input)
-- make only the one we clicked red
gui.BackgroundColor3 = Color3.new(1, 0, 0)
end
UserInputService.InputBegan:Connect(function(input, gp)
if not gp and input.UserInputType == Enum.UserInputType.MouseButton1 then
-- reset everything to white
for g in pairs(clickable) do g.BackgroundColor3 = Color3.new(1, 1, 1) end
local pos = input.Position
local under = playerGui:GetGuiObjectsAtPosition(pos.X, pos.Y)
local top = under[1]
if clickable[top] then OnClicked(top, input) end
end
end)
You could extend it to loop through the list for the first GUI that you track, instead of just the first one on screen.