Search code examples
godotgdscriptgodot4

Area2D inconsistent behaviour - Not functioning in main game scene, but functions fine independently


I was trying to make a set of Area2Ds that would display a value when the mouse is hovering over them. They're children of a control node (technically children of buttons in that node, the exact order is Control > Panel > Panel > Vbox > Hbox > Button > Area2D, yes the two panels are not a typo, no I'm not good at UI design). The control node has it's own scene where I edit it, and when I run just that scene everything works fine. The issue arises when I bring it into my main scene, everything loads correctly, but the Area2Ds no longer work. I have absolutely no idea why this is.

I've checked that the collision layers of the Area2Ds and the mouse tracker aren't somehow changed, I've confirmed that the Area2Ds aren't being deleted or moved out of place or having their collision shapes changed, I've tried changing the Mouse Filter setting in the control node since I saw someone with a (seemingly) similar issue who was able to solve it that way. No luck

Any help on figuring this out would be massively appreciated, thanks.

Alternatively, if there's a way for buttons to send a signal when the mouse is hovering over them that would also be useful.


Solution

  • I have to assume either something is up with the coordinates, or the mouse event simply won't reach the Area2D inside a Button (in fact, it sounds likely to me that the Button shallows these events).

    Regardless, you should NOT need an Area2D for this.


    First of all, I should also note that Control has mouse_entered and mouse_exited signals. However, they will consider that the mouse exited when it is hovering a child Control, which might or might not be what you want.


    Since you are working on a Control, I suspect what you want is _gui_input. You can get the mouse position in local coordinates in _gui_input like this:

    First make sure the InputEvent you get InputEventMouse:

    func _gui_input(event: InputEvent) -> void:
        var mouse_event := event as InputEventMouse
        if mouse_event != null:
            var local_mouse_pos = mouse_event.position
    

    You could emit your own signal from there, or do whatever you need.


    Assuming _gui_input does not work for you, then you could use good old _input. The caveat is that the position you get is in viewport coordinates, so it requires a conversion:

    func _input(event: InputEvent) -> void:
        var mouse_event := event as InputEventMouse
        if mouse_event != null:
            var local_mouse_pos = make_input_local(mouse_event).position
    

    Here make_input_local will convert from viewport to local coordinates of the CanvasItem (which includes all Node2Ds, such as Area2D... And also Controls, such as a Button).

    And again, you could emit your own signal from there, or do whatever you need.


    If everything else fails...

    From a CanvasItem you can use get_local_mouse_position to get the mouse position in local coordinates.

    var local_mouse_pos := get_local_mouse_position()
    

    Then you can check if that position is inside the Control's size:

    var local_mouse_pos := get_local_mouse_position()
    var hovering := Rect2(Vector2(), size).has_point(local_mouse_pos)
    

    You could run that code in _process so you have it updated every frame. And using that information you can emit a custom signal or whatever you need.