I'm really struggling to understand how
InputEvents propagate in Godot. Here's what the scene tree looks like in my little 2D demo project:
PanningControl node is the only node that's part of the user interface. As you can see from the screenshot, the bounding box of this control node takes up most of the visible screen. The idea is that at some point this node will listen to
mouse_exited signals, and then do some things in the appropriate callbacks to move / pan the camera around the 2D game world - this isn't so critical for my question, but that's why it's there, and that's why it's covering most of the screen.
The script attached to this node looks like this:
self.mouse_filter = Control.MOUSE_FILTER_PASS
print("In PanningControl._gui_input -> ", event)
The only other really interesting node in the scene tree is the
Player, which consists of a Sprite and an
Area2D node with a
CollisionShape. I want the Player to be pickable by clicking with the mouse using Ray-Casting. According to the documentation's Ray Casting example, this is trivial to implement using a
Area2D inherits from
CollisionObject has an “input_event” signal that will let you know when it was clicked
I realize the example uses 3D instead of 2D. Same difference.
Here's the script attached to the
self.input_pickable = true
self.connect("input_event", self, "on_input_event")
#func _input_event(viewport, event, shape_idx):
# print("In Area2D._input_event -> ", event)
func on_input_event(viewport, event, shape_idx):
print("In Area2D._on_input_event -> ", event)
The code works identically whether I bind the
input_event signal to the
on_input_event callback, or whether I simply override the
_input_event callback (this one does seem to take precedence, however, so I would use one or the other, but not both).
This is where my question really starts. If I hide / disable the
PanningControl and start my
Game scene, any mouse related events (such as
InputEventMouseButton) are captured by the
Area2D - I know that to be true because I can see the output of my print-statements in the log anytime I hover over- or click on the
However, if I DON'T hide / disable the
PanningControl (because I need it to be there, obviously), so that it is covering most of the screen again (also covering the
Player completely), the same mouse-related events are now captured by the
PanningControl - the events are not propagating to the
Player, and the
Player no longer sees them.
I understand that this is more-or-less the "desired" behavior. I've taken a look at the InputEvent documentation, which explains that user interface controls are notified of events first (makes sense). However, I simply cannot understand how events actually propagate. As you can see I've set the
mouse_filter property to
pass, so that it does not consume events - but where do they go, then? Some other threads I've seen seem to suggest that events only propagate to the immediate parent, and not to any siblings - huh???? Well then how am I supposed to get my events where they need to go??
PanningControl needs to capture mouse input to do its panning-duties, and I also need to be able to Ray-Pick my
Player. It's almost as if the events are either being consumed when they shouldn't, or they're propagating somewhere that can't reach the
Player (more likely) - how can I make them reach the
Player? Also, before you suggest simply using
_input for my
Player - I insist upon using the
input_event signal or
_input_event callback because it simplifies Ray-Picking - I don't see why I should have to downgrade to using
_input just to filter through all events and figuring out which ones are useful for the
I feel like I'm forgetting something, but at this point my brain is fried, and I've spent too much time on this problem as it is. I hope that I've explained my thinking clearly enough so that someone who knows the internals of
InputEvents can correct my (apparently flawed) intuition for how this system works (that being said, according to my intuition, it's unintuitive and doesn't work).
Thanks for your time.