Godot: Nodes receive _input in an unintuitive order

Created on 15 Aug 2018  路  10Comments  路  Source: godotengine/godot

Godot version:
3.0.6 (Steam)

Issue description:
Currently, the order in which nodes receive non-gui input events is undocumented, but appears to be by reverse depth-first - ie. up from the bottom of the scene tree UI. For example:
gd-input-order

To me, this is counter-intuitive. While bubbling up makes sense for GUIs, which have a specific "active" control, general events which are not targeted at any one node I would expect to spread starting from the root.

I feel like a scene's root node should be able to capture events and prevent them from passing down to its children. But as it currently stands, to intercept some or all of a scene's input events, a scripted node must be created (and maintained) at the end of the scene tree.
(Intercepting _unhandled_input using _input is not viable, as this also interferes with UI events.)

I'm willing to be the one to clarify the docs, but I'd first like to know if this was an intentional choice (so I can understand the reasoning), or if it is something which we think can/should be changed?

discussion core

Most helpful comment

To me, this is counter-intuitive. While bubbling up makes sense for GUIs, which have a specific "active" control, general events which are not targeted at any one node I would expect to spread starting from the root.

I wouldn't expect this. My expectation is that it would work the same for Control and for other nodes. Down the tree they are more "in front" of the other objects, so they receive the input first. The root then can have a generic catch-all handler that can assume "if the event got here, it missed all my children".

It might be useful to have another option for the input traversal, but I don't think it is "the most intuitive" nor that it should be default.

All 10 comments

Have you checked this docs on InputEvent? It's a bit out of date but should answer some questions. Also get_tree().set_input_as_handled() stops the propagation.

I'm personally OK about how input works, except there is one use case for which I still have issues with, and it relates to being able to interrupt events going under a given parent: #20234

Have you checked this docs on InputEvent? It's a bit out of date but should answer some questions.

Yeah, I've read that page, as well as the relevant class reference docs, and even the related source code! The propagation from _input to _gui_input to _unhandled_input is made clear, but nowhere have I found any mention of the order in which non-GUI events propagate from one node's handler to another within a scene.

It is mentioned specifically for GUI events ("Events that are not consumed will propagate up, to Control鈥檚 ancestors") because people filed issues thinking it was broken. So now I'm filing an issue because I think the other two actually are broken! :D

Also get_tree().set_input_as_handled() stops the propagation.

Yes, but that doesn't help a scene's root node, because all of the other nodes in the scene will already have received the event by the time it arrives at the root. Which is the issue at hand.

To me, this is counter-intuitive. While bubbling up makes sense for GUIs, which have a specific "active" control, general events which are not targeted at any one node I would expect to spread starting from the root.

I wouldn't expect this. My expectation is that it would work the same for Control and for other nodes. Down the tree they are more "in front" of the other objects, so they receive the input first. The root then can have a generic catch-all handler that can assume "if the event got here, it missed all my children".

It might be useful to have another option for the input traversal, but I don't think it is "the most intuitive" nor that it should be default.

I wouldn't expect this. My expectation is that it would work the same for Control and for other nodes. Down the tree they are more "in front" of the other objects, so they receive the input first.

Hmm, that reasoning certainly works for 2D, so I guess my line of thinking comes from all my recent work being in 3D, where a node's position in the scene tree bears no relation to any concept of "in front" or "behind".

I think front/behind in 3D is handled by pickable collision objects, in that case the frontmost object receives input first (not _input though, see doc about this one)

I don't think anything should be changed here. Child-to-parent makes sense to me, even outside of 2D, I would expect children to "override" parents. The other way around would be less useful.

In any case, hopefully you are not writing too much code that depends on execution order. Race conditions are not good.

So I guess my thinking was that the root node is the only node in a scene which represents the scene as a whole, and has control over every node in the scene. Thus it should be able to control what its child nodes can see and do, and how they interact, including being able to deny child nodes access to certain input events.
It bothers me that the only way to achieve this right now is with careful management of the order of nodes in the scene tree.

For comparison, JavaScript has the concept of the "capture" phase of event handling, where events travel down the tree first, before then bubbling up. An event handler can be triggered on the capture phase, and can stop propagation there to prevent bubbling from ever starting.
But I guess that would be another feature request entirely!

So since I appear to be in the minority in regards to the issue in hand, and nobody's ridden to my rescue as yet, I'll keep this issue open until I get around to adding some clarifying info to the documentation. Unless someone else gets there first. :)

@romlok It would be good if you added the image in OP to the documentation.

I would write something like "In accordance with Godot's node-based design, child nodes receive calls at a higher priority than the parent nodes. This allows child nodes to override the parent nodes" and something about this being ideal for GUIs with child buttons having priority focus.

(It's in accordance with Godot's design because child nodes can be like "components" in other engines)

Okay, I've created a couple of pull requests to clarify this in both godot-docs and the class reference.

Thanks for the feedback everyone. :)

Was this page helpful?
0 / 5 - 0 ratings