0 votes

Hi there,

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:


The 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_entered and 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:

extends Control

func _ready():
    self.mouse_filter = Control.MOUSE_FILTER_PASS

func _gui_input(event):
    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 CollisionObject (Area2D inherits from CollisionObject2D):

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 Area2D node:

extends Area2D

func _ready():
    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 InputEventMouseMotion and InputEventMouseButton) are captured by the Player's 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 Player.

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 PanningControl's 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 Player.

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.

in Engine by (14 points)

Just a note, your screenshot is not displaying.

Please log in or register to answer this question.

Welcome to Godot Engine Q&A, where you can ask questions and receive answers from other members of the community.

Please make sure to read How to use this Q&A? before posting your first questions.
Social login is currently unavailable. If you've previously logged in with a Facebook or GitHub account, use the I forgot my password link in the login box to set a password for your account. If you still can't access your account, send an email to webmaster@godotengine.org with your username.