GuiButton User Input: Buggy behaviour workaround

This is my first tutorial and it gives me shivers to think about the feedback. Nonetheless, here goes.


I. Briefing

Hello, fellow developers and aspiring developers. Some of you may already know how to do what I am about to explain, but I’m here to keep this on record to indoctrinate those who don’t know how, and to keep personal record for future references.

Earlier today, as I was working on a project for a group, I was creating a main menu and wanted to give my buttons the spice they require so they are not stiff or flavorless. As I was writing out the hover/leave functions for the buttons, I remembered that MouseEnter (in particular) is extremely buggy.

For a while, MouseEnter has been a problem for Roblox buttons. You just appreciate that Roblox provides MouseEnter/MouseLeave so you don’t need to spend time writing out custom logic. You get down to the testing phase, hover over your button and watch as it emits the effects you like. You click on that button, move your mouse a bit and WHOA, it’s doing something unexpected - re-running your Enter function!

So; how do you fix this? How do you get your buttons to only run MouseEnter when the mouse is truly hovered, and MouseLeave when the mouse departs from the button? Look no further, here we go!


II. Setting Up

Now the first thing you’re going to want to do is, of course, find your button(s). For the purposes of this tutorial, I’m going to smack one in the middle of the screen and hope you can follow along.

EUNaLycsQJ2iqLt-0_BI-Q

Wonderful. Now all that’s left is to get to the code. Create a LocalScript anywhere you want within the Gui, preferably in the ScreenGui since you should aim to control Guis using one script (in my opinion).

Now, I’ll be showing you two kinds of code; the one that will produce buggy behavior, and the difference if you apply the method I describe here. This will help you identify what I’m trying to convey and spot the difference.


III-I. Commonplace Method

View

In the script you made, open it up and write something. Here’s what I wrote:

Code
local Button = script.Parent:WaitForChild("TextButton")

Button.MouseEnter:Connect(function ()
	Button.Text = "hovered"
end)

Button.MouseLeave:Connect(function ()
	Button.Text = "left"
end)

Button.MouseButton1Click:Connect(function ()
	Button.Text = "clicked"
end)

Once that’s in, exit the script editor and hit F6 on your keyboard to start a Play Solo server. Hover over your button, then hover out of it. Notice how it works as intended? Now, for the second time around, hover over your button and click it. Notice how this too work as intended? Now move your mouse just a bit. Oh no! It says “hovered” when you were already hovered over the button!

https://thumbs.gfycat.com/InfiniteMarriedIberianlynx-size_restricted.gif

This is the kind of stuff you preferably should stay away from. Sometimes it’s super annoying to look at when the button is producing improper effects.

Well then, if you don’t like that happening, look to the next method.


III-II. “Solution Method”

View

To start this off, we’re going to make a BoolValue in the TextButton. If you can handle it, you could probably put it in the script itself, but we’re not aiming for super advanced here. Give it a whacky name, edit it as you please, but it must be a descendant of the button and the value must be false. No one starts off auto-hovered over a button… right?

-lybs434T56KaSCk0stOdQ

Next, we’re going to open our script. The bool will be serving as the main part for determining the state of the mouse (so that MouseEnter isn’t fired again after clicking), with the events only being there to support. Write some code in it along these lines:

Code
local Button = script.Parent:WaitForChild("TextButton")
local Hovered = Button:WaitForChild("Hovered")

Button.MouseEnter:Connect(function ()
	if Hovered.Value == false then
		Hovered.Value = true
	end
end)

Button.MouseLeave:Connect(function ()
	if Hovered.Value == true then
		Hovered.Value = false
	end
end)

Button.MouseButton1Click:Connect(function ()
	Button.Text = "clicked"
end)

Hovered.Changed:Connect(function (value)
	Button.Text = (value == true and "hovered" or "left")
end)

Now, follow the steps in the “Commonplace Method” for testing. Press F6 to start a Play Solo server. Hover over the button, then unhover it. Works fine? Goodie. Hover over it again and click this time, then move your mouse slightly. GASP! It doesn’t say “hovered” again!? Joy!!!


IV. Conclusion

So yeah, the bug regarding MouseEnter/MouseLeave bothered me for a while and I decided I could make a workaround by doing this. I was so overjoyed that I found a solution to my TextButton problems that I just had to share it somewhere, and the DevForum was that somewhere. I hope that you have been able to take something away from this, even though you probably already knew how to do this. :slightly_smiling_face:


I wrote this at around 12:45 AM - 1:45 AM EST, so I may not be getting my information right or I may be providing inadequate information. Let me know if there’s a problem and I’ll address it as soon as I can.

11 Likes

You can also use InputBegan/Ended to do this:

Button.InputBegan:Connect(function(Input)
    if Input.UserInputType == Enum.UserInputType.MouseMovement then
        -- code
    end
end)

Same thing with InputEnded.

I’ll compare them, anyway

7 Likes

oof

If I knew that existed, I wouldn’t have made this post. InputBegan/InputEnded is probably more reliable than using the Mouse events, but I’ve been with them for so long that it’s habit to use them and I’m unable to break that habit to move on to “better practices”.

4 Likes

Every time you see yourself using them, catch yourself, and redo your work with the better method if possible.

This will help you break the habit easily :slight_smile:

4 Likes

We’re planning on addressing the unreliability of these events early next year hopefully. But in the mean time, workarounds are often still required.

8 Likes

I have to bring this thread back from the dead one last time for the sake of closure.

At the time that this thread was created, GuiButtons often had this strange issue in regards to hovering after clicking. This thread attempted to explain a way to mitigate that problem by essentially creating a flag determining whether an item was hovered over or not, then utilising that to determine whether to run hover/leave functions or not.

This method is heavily outdated and should not be used. The way I did it in itself is pretty ugly, which is by using ValueObjects instead of an internal value. Those of you who may have stumbled across this thread searching for an answer to a problem you may have had, this is not a good answer anymore. Do not rely on this.

Between the time of this thread’s creation and now, the Gui Engine was rewritten. It fixed this issue (if I remember correctly) and it overall functions much better than it did in the past. This is no longer something you have to worry about.

Typically nowadays, you can use the mouse functions as normal or use a solution similar to what EmeraldSlash posted, which is using the input functions on a GuiButton. Think of those like UserInputService that’s specific to a certain Gui element.

Button.InputChanged:Connect(function(Input)
    if Input.UserInputType == Enum.UserInputType.MouseMovement then
        if Input.UserInputState == Enum.UserInputState.Begin then
            -- Hover code
        elseif Input.UserInputState == Enum.UserInputState.End then
            -- Leaving code
        end
    end
end)

And now that I’ve said that, this thread can finally fade away. I apologise for bringing it back after two years, but again, this thread needs closure. This outdated tutorial may now be put to rest for another more years to come.

5 Likes