Understanding InputEvent propagation?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Frustrated

Prelude
I’ve got a scene tree that looks like this:

(The last time I tried to embed a picture in my post, it didn’t work, and my question got no attention because of it, so here is an Alternative Image Link in case the embedded image is not visible).

And, in case that link expires, here it is visually:

Game[Node2D]
|
+-->Camera2D
|
+-->PanningControl[Control]
|
+-->Area2D
   |
   +-->CollisionShape2D

The PanningControl node (orange outline) covers most of the visible screen, and completely obscures / covers the Area2D node (blue square). The script attached to this node looks like this:

extends Control

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

func _gui_input(event):
	print("In PanningControl._gui_input -> ", event)

The Area2Dnode (blue square) sits in the middle of the visible screen. The script attached to this node looks like this:

extends Area2D

func _ready():
	self.set_process_input(true)
	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 _input_event callback has been commented-out temporarily just for testing.

The Game node also has a script attached to it, but it is not relevant. It is just:

extends Node2D

func _ready():
	pass

The behavior I’m experiencing
If I launch the Game scene, and move the mouse / cursor so that it is hovering anywhere over the PanningControl node’s bounding box (orange outline), I can see the print-statements from PanningControl._gui_input executing. Even if I hover the mouse over the Area2D node (blue square), I only see the print-statements from PanningControl._gui_input.

If I relaunch the Game scene, only this time, hiding / disabling the PanningControl node, when I hover the mouse / cursor over the Area2D node (blue square), I see the print-statements from Area2D.on_input_event executing.

The behavior I expect / want (my actual question)

I would like for the Area2D.on_input_event or Area2D._input_event callbacks to be triggered when the mouse hovers over or clicks on the Area2D, even though the PanningControl node is enabled / visible / obscuring the Area2D node. The documentation says that the _input_event callback has least priority, and will be triggered last if no other higher-priority callbacks have consumed the event yet. I have explicitly configured my PanningControl node NOT to consume events, so what gives? I have some pretty major gaps in my knowledge when it comes to the inner workings of GODOT, so don’t hesitate to point out major flaws in my thinking.

How can I achieve my desired behavior? Also, I know I could simply override Area2D._input, but I don’t want to do that, since then I would be polling input, rather than using the event-based _input_event callback, which is desirable. Thanks for any feedback.

I tried researching a little your problem since i found interesting. But it seems there is 2 different problems with what you want to do:

  1. The MOUSE_INPUT_PASS does not allow the event to be passed to siblings, what it actually does is passing the event to the ancestors. This would mean to do what you want you would have to make Control/Area2d children of each other, source

  2. I’m not sure why but there is a different relation between a Control and an Area in the way they treat events. It seems that having a Control handle the event completely stops the Area from receiving the mouse event. I had no time to look deeper into this, there might be a way but more research is needed.

In short, making your Area2D into a Control type node and making it the parent of Control will make the event propagate. But there is probably a reason you want them to be the types they are, and I can’t suggest any workarounds without knowing what you are trying to accomplish.

Good luck!

tastyshrimp | 2019-12-17 23:48

@tastyshrimp Thanks for the feedback and taking an interest in my problem. I think you hit the nail on the head with point #2. The PanningControl node is a simple GUI control node which at some point will allow me to pan the Camera2D around the game world - the idea is that when the mouse is outside of the PanningControl node (when the mouse_exited even is triggered), the camera starts panning in the direction of the cursor (relative to the center of the screen). When the cursor is inside the PanningControl (when the mouse_entered signal is triggered), the camera stops panning.

It would not be appropriate for theArea2D node to be a GUI element, unfortunately, because it’s meant to be part of the game world - imaging a ray-pickable game world object or sprite.

Frustrated | 2019-12-19 18:27

Cant you try to work around it by making 4 panning controls, one for each quadrant, and keep them outside your play area so you never have the Area2D and PanningControl keep on the way of each other.

Or go completely crazy and detect when the mouse is over an Area2D to deactivate the PanningControl or make it ignore the mouse input so the event reaches the Area2D. I believe you can do that by using the get_global_mouse_position and checking if it is super imposing an Area2D. Combine that with VisibilityNotifier2D to know which areas to check and it might just work.

tastyshrimp | 2019-12-19 20:40