Get closest node in tree order of overlapping nodes

Godot Version

4.2

Question

Imagine a few overlapping Area2D. Perhaps a deck of cards. I want to get the top card of the pile. Simple, right?

If I “click” it using intersect_point, I get all the results in a non-deterministic order. Yet the cards are drawn in SceneTree order since they are CanvasItems. From here I could…

  1. Get the tree order of the results. But how? I cannot find such an in-built method, and walking the entire scene tree each physics frame for each result to manually calculate tree order seems like a pain.
  2. Check Z-index. Except this requires manually assigning Z index to every item in the game and keep it correct. No thank you.

It seems like such a simple task for a game engine; “click the thing on the screen”. Surely I am just missing it in the docs or something?

On a related note for context, see disussion 8965 and issue 1058.

You can get the index in the tree of the node with Node.get_index()

Example:

		var intersections = direct_space.intersect_point(query)
		if intersections:
			intersections.sort_custom(func(a, b):
				var obj_a = a.collider
				var obj_b = b.collider
				return obj_a.get_index() > obj_b.get_index()
			)
2 Likes

Interesting idea! Index is useful for siblings, which might be good enough, even though it is not strictly tree order. Consider e.g. the following tree:

A
Aa Ab
B
Bb

The node Aa, child of A, has index 0.
The node Ab, also child of A, has index 1.
The node Bb, child of B, has index 0.
Bb has lower index than Ab, but Bb renders in front of Ab because of tree order.

I guess one could calculate all indexes going from root to bottom, and from that get tree order. Which sounds like the pain I mentioned in point 1, but perhaps it is not necessarily so bad computation wise. Using index could also work if the game is designed to exploit some special relationships (e.g. all relevant nodes are siblings).

For that use Node.is_greater_than()

		var intersections = direct_space.intersect_point(query)
		if intersections:
			intersections.sort_custom(func(a, b):
				var obj_a = a.collider
				var obj_b = b.collider
				return obj_a.is_greater_than(obj_b)
			)
1 Like

Did a few simple tests, works like a charm. Looks like I did miss it in the docs! Cheers! :partying_face: