Player’s Mouse.X and Mouse.Y return wrong values only while in the scope of .MouseEnter/Leave/Moved events of gui buttons

Note: I initially posted this to the scripting help forum. I’m crossposting here because this is a genuine engine bug.

Background:
I came across a VERY weird bug while coding a popup screengui for a billboard gui. The initial goal was to have extra info pop up over the player’s mouse when they moused over a button on a certain billboard gui.

What happened:
Whenever you get the values from the player’s mouse, you get wrong values while in the scope of a .MouseEnter or .MouseMoved event.

local mouse = game.Players.LocalPlayer:GetMouse()
someTextButton.MouseEnter:Connect(function ()
    local mousePosX, mousePosY = mouse.X, mouse.Y --would give wrong values
end)

The most interesting part of this, is that if you instead get the mouse values from a different scope, like a coroutine at the end of the function, you get the correct values.

local cooroo = coroutine.wrap(function ()
    while true do
         print(mouse.X..","..mouse.Y) --would print the CORRECT mouse position
         wait(1)
end)
cooroo()

Demonstration:
I set up a file that shows the problems with getting the correct mouse X and Y values. There are two guis, a billboard gui that is adorned to a part in workspace and a screen gui that will be positioned at the mouse when you hover over the billboard gui’s button. The gui should be wrongly positioned somewhere else in this demo.

In the console you should see the mouse values printed from both the .MouseMoved event and the looping coroutine. mouseProblem.rbxl (17.9 KB)

Also here’s a screenshot if you don’t want to download the file for the source code:

3 Likes

MouseEnter and MouseMoved events give you the X and Y coordinates of the mouse as inputs. Is there a reason you are grabbing the X and Y from the mouse object instead of from the event?

image
developer.roblox.com/api-reference/class/GuiButton

They also give the wrong X,Y values in my tests

Edit: here’s my example, it gives the same result mouseProblem gui events.rbxl (18.0 KB)

They return relative position, not absolute.
Although, it’s a bit bugged due to topbar.

To get correct relative position:

local x,y = GuiButton.MouseEnter:wait()
y = y-36 --> Topbar is 36 pixels in height.
local RelativePosition = UDim2.new(0, x, 0, y)

To get absolute position:

local x,y = GuiButton.MouseEnter:wait()
y = y-36
local GlobalPosition = GuiButton.Position + UDim2.new(0,x,0,y)

You might want to transfer these values to pixels instead of scale, which can be done by multiplying X.Scale with Gui.AbsoluteSize.X and Y.Scale with Gui.AbsoulteSize.Y.

Have you considered using a buffer to get the mouse coordinates outside the context of the event?

local x,y
local RS = game:GetService("RunService").RenderStepped
local mouse = game:GetService("Players").LocalPlayer:GetMouse()
coroutine.resume(
    coroutine.create(
        function()
        while true do
            x = mouse.X
            y = mouse.Y
            RS:Wait()
        end
    )
)

---

someTextButton.MouseEnter:Connect(function ()
    local mousePosX, mousePosY = x,y
end)

someTextButton.MouseMoved:Connect(function ()
    local mousePosX, mousePosY = x,y
end)

another option would be to use the hidden feature of InputObjects (the fact that they are mutable and have a .Changed event)

Even if the PlayerMouse gives relative values inside MouseEnter/Leaved/Moved, this is unexpected behaviour. From the Wiki:

int X [readonly] [notreplicated] Describes the X (horizontal) component of the mouse’s position on the screen

The difference in behaviour is confusing and makes it impossible to create general-purpose functions using PlayerMouse (that can be used from the events as well as outside them).

That being said, it would be best not to break backwards compatibility with games that rely on this behaviour, so I think it would be best to use UIS.

3 Likes

Its 2020, and nobody has fixed this issue. I’ve been using mouse move to get the x,y of the mouse and use it to make a overlay feature. And when I tried testing how it worked on different resolutions, it completely broke for other resolutions. I have to use uncanny methods to fix this issue, and this should not be such a big problem. Roblox, please fix.

2 Likes

My bad, Its not a issue with this specific topic. Its something to do with UI scale and UI Corners, if I’m correct, its pretty weird and its another bug.

Nah, It is 2020 and this is still an issue. There is still no documentation about this behavior, which makes me sad. I just realized how many bugs from my old game are because of this purposely hidden bug. I just now stumbled upon this and we are literally having trouble trying to work around it.

1 Like

For those of you going through the same intended bug, this is a hacky way to do what you would expect the mouse.X and .Y to do, if you have a better way please speak up!

local MousePosition = Camera:WorldToScreenPoint( Camera.CFrame.Position + Mouse.UnitRay.Direction )
1 Like

I am also having problems with this. Here is my code:

local function start()
	local start = UIS:GetMouseLocation()
end
script.Parent.Top.InputBegan:Connect(start)

If you print the variable “start”, it will always print the mouse position based on the relative position of the BillboardGui. This is making is very challenging to create a slider inside a BillboardGui, because we are unable to get the correct screen position of the mouse.

In the documentation, it says that UserInputService:GetMouseLocation() returns the position of the mouse on the screen, however, when clicking an element in a billboard GUI, it returns the wrong value.