Area2D pickable and event bubbling issue

Godot Version

v4.5.stable.official [876b29033]

Question

I’m trying to setup a scene for a 2D top-down game where I can handle a click on the world to move the player and handle click on enemies to set them as target.

I seem to be unable to stop the event from propagating from the mob up to the world.

This is the code I’m using on the World scene:

extends Node2D

func _unhandled_input(event: InputEvent) -> void:
	if event.is_action("select") && event.is_pressed():
		$background.visible = true;
		await get_tree().create_timer(0.5).timeout;
		$background.visible = false;

This is the code I’m using on enemies:

extends Node2D

func _ready() -> void:
	$Area2D.input_event.connect(onInput);

func onInput(viewport:Viewport, event:InputEvent, shapeId):
	if viewport.is_input_handled(): return
	if event.is_action("select") && event.is_pressed():
		viewport.set_input_as_handled();
		$overlay.visible = true;
		await get_tree().create_timer(0.5).timeout;
		$overlay.visible = false;

From my understanding, the event should be stopped before reaching “_unhandled_input” but it does not.

I’ve uploaded a video here to show this in action (I’m clicking on the overlapping part of sprites): Video Unavailable

I’ve also tried to replace the Area2D with Controls, but I incur in issues with z-index when Y-sorting is enabled so it is a no-go.

To add on this, when two Area2D overlap, the event seems to fire randomly on one or another as you can see by clicking on the intersected area between sprites.

How should I handle this?

Physics objects picking happens last, after all input event handlers have already been executed and none of them called Viewport::set_input_as_handled() Look at the diagram and descriptions in the docs:

Order of physics objects is not guaranteed when picking them. By default it doesn’t follow the order of nodes in the tree and can change frame to frame. To sort them, enable Viewport::physics_object_picking_sort and, if needed, Viewport::physics_object_picking_first_only

Thank you, I tried with

Viewport::physics_object_picking_sort and Viewport::physics_object_picking_first_only

but didnt work because of Y-sort enabled on nodes (I always got the last node in the tree and not the one in “foreground”).

I ended up using this function to get the clicked area that takes into account the Y position. In my case it works well:

func get_top_area_at_position(position):
  var point = PhysicsPointQueryParameters2D.new();
  point.position = position;
  point.collide_with_areas = true;
  var hits = get_world_2d().direct_space_state.intersect_point(point, 10);
  var top = null;
  for hit in hits:
    var node = hit.collider;
    if top == null || node.global_position.y > top.global_position.y:
    top = node;
  return top;

z index only determines drawing order, not input order. Input order is determined by the position in the tree.