godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
90.99k stars 21.16k forks source link

2D selecting ignores `z_index` #96576

Open KoBeWi opened 2 months ago

KoBeWi commented 2 months ago

Tested versions

4.3 4.2.2

System information

Windows 10.0.19045 - Vulkan (Forward+) - dedicated NVIDIA GeForce GTX 1060 (NVIDIA; 31.0.15.4633) - Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 Threads)

Issue description

If lower node z-index wise is lower in tree, it will be selected through the top node:

https://github.com/user-attachments/assets/8e5ee496-b003-4588-b08f-1547a3bc247d

Steps to reproduce

  1. Add 2 Sprite2Ds
  2. Make them overlay
  3. Change z-index so that sprite further in tree appears below (contrary to default drawing order)
  4. Click intersection point

Minimal reproduction project (MRP)

N/A

bikemurt commented 2 months ago

https://github.com/user-attachments/assets/5773156e-370b-483c-9304-4b540ff1de85

I think this should fix it for single clicking. I didn't look into group selections or anything like that, but I'm not sure that's relevant with z-index issues.

kleonc commented 2 months ago

2D selecting ignores z_index

It ignores rendering order in general. So all properties like show_behind_parent, top_level, y_sort_enabled are not taken into account as well. Same about containing canvas layers.

For all of them it's as trivial to create an example of "wrong selection" as for z_index.

Also note that z_index is considered separately for each rendering canvas; meaning a canvas item with greater z_index can still be drawn behind a canvas item with lower z_index (if they belong to different canvases, e.g. different CanvasLayer nodes).

KoBeWi commented 2 months ago

Well my original problem was with TileMapLayer. I have a foreground layer and background layer and the back layer was being selected over the front one. It's rather annoying.

TheSofox commented 1 month ago

I looked into this and found it more complex than anticipated.

As covered, we have to look at several different attributes. Since I figured the RenderingServer was already doing all this, I looked there. The problem is, RenderingServer is all about setting information, not retrieving it. You canvas_item_set_z_index() and canvas_item_set_sort_children_by_y(), but you don't retrieve these values. It's one way, at least for the majority of canvas_item functions

For my PC, the RenderingServer seemed to be RendererCanvasCull which calls _render_canvas_item_tree() which recursively calls _cull_canvas_item() which in turn uses _attach_canvas_item_for_draw() to add each CanvasItem to a list ready to be rendered.

The order in which they are added to the list (z_list) seems to be what determines the order that they're rendered in. The problem is that I don't know how to access this order. RenderingServer doesn't seem to have an API to access it.

Likewise, I don't know of any API that can, for instance, tell if one CanvasItem is in front of another. To see if I could find one, I did a search of draw_behind_parent but mostly only found the sections of code I've mentioned above.

So either we find a way to crack open the RenderingServer, or we recode all the functionality in a separate function, maybe one that sorts a small list of CanvasItems

bikemurt commented 1 month ago

There was a short discussion here about a bandaid solution:

https://github.com/godotengine/godot/pull/96584#issuecomment-2332307061

I'll try to double check tomorrow to see if the RenderingServer exposes any information regarding the absolute z order.

Besides the Godot editor, I imagine there are other usecases for knowing the renderered z order.

To me an API of some sort would make sense. Any issue with having the RenderingServer set a field in each node containing the proper sort order as rendered?

TheSofox commented 1 month ago

Some potential solutions

  1. (As suggested) RenderingServer edits the CanvasItem to include their absolute z order. Feel unsure about this because it does it in secret
  2. Edit RenderingServer API to include canvas_item_sort_absolute_z that takes a list of CanvasItem and sorts them front to back.
  3. Edit RenderingServer API to include canvas_item_get_frontmost that takes a list of CanvasItem and returns the CanvasItem that's in front of all the others on that list.

I could potentially code up any of the above solutions, but since we're dealing with API that's used by more than one class and appears in documentation, I'd want some backup on which option to go for.