Godot: Area2D mouse_entered and mouse_exited signals not called in Viewport

Created on 23 Feb 2019  路  16Comments  路  Source: godotengine/godot

Godot version:

Tested with 4f697f73a5ac567c1dc22ce44a7fab98f619dfe3

OS/device including version:

Linux Mint 19.1 tessa 64 Bit

Issue description:

When using Area2D in a Viewport the mouse_entered and mouse_exited signals are never called.

Steps to reproduce:
Use the test project.
When you run the project you see two godot-icons. Both icons are unmodified instances of Area2D.tscn.
The one on the left is contained in a Viewport and does not print any messages when it should receive mouse enter and exit signals.
The one on the right will print messages when the mouse enters/exits the icon.

Additional info:
The UI node's mouse filter is set to pass.
The ViewportContainer node's mouse filter is set to pass.
The Viewport's setting Handle input locally was tested enabled and disabled.
The Viewport's setting Disable input is disabled.
The Viewport's setting Own World was tested enabled and disabled.

Minimal reproduction project:

ViewportIssue.zip

discussion documentation physics

Most helpful comment

Still broken. The workaround does not work. _input_event never fires for an CollisionObject2D node in a viewport.
Use case example: A "map" renders to a viewport in a viewportContainer. The use of a viewport allows to scroll the map by moving the camera.

This bug (yes, it's a bug) makes it impossible to select a unit, building, city, etc... by clicking on it.

All 16 comments

It seems that all signals from CollisionObject2D are affected, because the signal input_event is also not called in the viewport instance.

That is expected behavior. Picking works with unhandled events and currently ViewportContainer pass to its Viewport as unhandled events only those really unhandled in the whole scene.

You need something like this in your Viewport:

extends Viewport

func _input(event):
    unhandled_input(event)

I'll send a PR that allows to have that working automatically, but I don't think it will do it into 3.1.0.

I'm using the latest version. It's still not working

@RandomShaper can we reopen the issue to track it? I just ran into the same issue and it doesnt seem obvious as to the end user why this would not work.

@giulianob indeed. It took me a lot of asking around for a problem that had no obvious solution, until I figured this ought to be an issue. In my case it involves 3D Areas.

Thanks for that workaround @RandomShaper.

Picking works with unhandled events and currently ViewportContainer pass to its Viewport as unhandled events only those really unhandled in the whole scene.

@RandomShaper I don't understand, in your example code you used _input, but this is not unhandled events, it's going to catch them all, even when you didn't actually click on the viewport, isn't it?

Also, if I click an Area2D inside a viewport, and the area doesn't react, what exactly is handling that input? And how to make sure it gets forwarded to that viewport and not to some other random script in the game that might have _unhandled_input? Because if input is unhandled so far in the enclosing scene, it might become handled after being checked by objects inside the viewport, and in such case, should not trigger _unhandled_input in the enclosing one.

@Zylann , I did have to turn input on/off manually for the viewport, using mouse_entered/exited signals on the parent control, or else it would still receive it while the mouse was over the UI around it.

I suppose this is also related to your other concern, since the input I'm using in the scene that's under the viewport is actually Area input_event signals (for mouse clicks and movement), which according to the docs is neither _input nor _unhandled_input and would normally be handled last.

@Zylann , I did have to turn input on/off manually for the viewport, using mouse_entered/exited signals on the parent control, or else it would still receive it while the mouse was over the UI around it.

I suppose this is also related to your other concern, since the input I'm using in the scene that's under the viewport is actually Area input_event signals (for mouse clicks and movement), which according to the docs is neither _input nor _unhandled_input and would normally be handled last.

I still can't get it to work. I tried the workaround and v3.2 alpha (someone else had mentioned that it was working in it). Perhaps I am not setting it up correctly?

It's a little hack but it helped me.

func _input(ev) -> void:

    get_viewport().unhandled_input(ev)

I still cannot get mouse_entered/exited signals to fire either inside a viewport of a viewportcontainer.

Edit:
I fixed my case. I had to change the area2d to be the child of a newly created control node. Then use the mouse enter and exit events of that control node instead.

Investigated this deeper today:
For reference, everything is working as designed, only the design is being difficult :p .
Assuming a viewport in a container, with object picking on and everything correctly setup.
What happens:

  1. User clicks in the viewport somewhere.
  2. the input is propagated upwards. Here lies the first blocker -> most likely a control will have mouse.filter 'stop' then, and things stop here.
  3. If the event wasn't stopped or handled, the viewport unhandled_input will be called.
  4. Only then does it create a deferred physics picking call.
  5. The deferred physics picking call triggers the collision Objects 'InputEvent', sending signals.

This has several flaws:

  • If you want to actually trigger the object picking, you generally need to manually trigger 'unhandled input' (as here), as there's basically always a node with a "stop" mouse filter.
  • Because the physics picking pass is async, input cannot be blocked by picking.
  • It also means that one cannot get a 'nothing was picked' input event easily.

So if you have a viewport with some items, and you'd want clicking somewhere on the viewport but _not_ on an item to trigger something, the only solution is to have another area behind those objects (from the camera POV) that gets picked. And when that happens, you can do things (possibly send an input event 'behind' for example).

I have to say, because object picking happens during physics processing, I can't see an easy fix to this (not a godot dev though, so perhaps you guys have an idea already).
Perhaps with object picking activated, viewports should 'hold' events for at least one physics-processing-frame, and then only bubble them upwards if nothing was picked. This would introduce some slight input lag, but it _would_ make the whole architecture much more predictable, ad you'd either get an object pick event, or a regular GUI input.

EDIT: also for the record, I think it's better to do hack the viewport container over the viewport, otherwise all inputs go through the viewport, meaning even clicks outside the viewport-container area can trigger picking. So do this instead in the ViewportContainer script:

func _gui_input(event):
    get_node('Viewport').unhandled_input(event)

I've got a similar issue with the latest version 3.2.1.stable.official of Godot, I tried the hack with the unhandled_input and it worked but now the mouse_exited signal is triggered all the time, even when I'm within the Area2D the mouse_exited event is fired all the time.

Same issue on 3.2. Connecting the mouse_entered signal from an Area2D to a script never fires that script function. Worked around the same way @nikitaab20 did. Could be my fault, I'm new to Godot.

Just faced the same problem, but instead of a viewport it was a Panelnode that wasn't propagating the signal to an Area2D node (not child of panel) BELOW (in the common parent tree), the unhandled_input trick solved it (indicating that it inherits the same problem from the viewport).

edit: noted it's not working properly since now each processed frame the mouse_entered and mouse_exited signals are emited (because the mouse location input may come as a new each frame now) instead of happening a single time in the occurred events

edit2: Input page says the Following :

  1. If no one wanted the event so far, and a Camera is assigned to the Viewport, a ray to the physics world (in the ray direction from the click) will be cast. If this ray hits an object, it will call the CollisionObject._input_event() function in the relevant physics object (bodies receive this callback by default, but areas do not. This can be configured through Area properties).

however I can't find any properties Area related to activate this callback reception.

Still broken. The workaround does not work. _input_event never fires for an CollisionObject2D node in a viewport.
Use case example: A "map" renders to a viewport in a viewportContainer. The use of a viewport allows to scroll the map by moving the camera.

This bug (yes, it's a bug) makes it impossible to select a unit, building, city, etc... by clicking on it.

I will add my voice that this seems like a bug or at the least like a very unintuitive issue. There's no reason why a Viewport would be handling input events for children nodes if the ViewportContainer has its mouse_filter set to ignore. It makes even less sense that it does not appear to be handling events going to control nodes, but only stuff going towards Area2D/CollisionShape2D. So having a mouse_entered() signal on a Panel will work, but the mouse_entered() signal on an Area2D parent of the same panel will not work.

Even more frustratingly, I don't understand how changing the _input to an _unhandled_input on the viewport script would help. The _input event is anyway received by the Area2D and CollisionShape2D node inside the viewport, it just doesn't trigger the relevant mouse signals of the CollisionShape2D. One would have to manually recreate the mouse signal logic by hand inside the _input(), no?

Was this page helpful?
0 / 5 - 0 ratings