||Topic was automatically imported from the old Question2Answer platform.
- I’m creating a 2D sliding game with sprites that can overlap.
- When a sprite is clicked, I set
z_index for all sprites so that no two ever have the same index and selected sprite has the highest
- I have a raycast (cast to 0, 0) under my cursor at all times to see what’s under the cursor.
The problem is that the raycast doesn’t alway see the top-rendered item. The documentation says it finds the “closest object” but that’s not always the top item. Seems to go by the Scene Tree order. I’ve also tried setting z_index for the raycast to high but I guess that’s doing nothing, if
z_index is only for rendering and not for selection.
Various signals with mouse entering and exiting don’t seem to help. Three questions, if I may:
- Am I right in thinking that
z_index is a render only parameter?
- What’s the easiest way of having the highest rendered sprite be the one that’s selected?
- Would this best be done as a flat 3D game so that there is a genuine “above” value on the y-axis?
Raycasts find the closest object in 2D space. Z-index is not part of 2D space, so trying to find top-most with a raycast makes no sense. If the ray has a null size it makes even less sense to do one in the first place. You could just as well use
intersect_point: Physics2DDirectSpaceState — Godot Engine (3.1) documentation in English
Z-index it’s only about rendering indeed, and is combined with
index in the tree to obtain their drawing order. Note that you could then disregard Z-index and simply re-order your node within its parent to simplify this.
Control nodes may be an alternative to your needs (because they know when they are hovered and clicked through input and they DO respect tree order), though I imagine it’s not necessarily fit depending on your game.
You may want to use a different collision detection method which gives you all shapes under your cursor (like
get_overlapping_bodies), then you can sort them to find the one with highest Z-order.
If you had a 3D game then you’d be able to do a raycast in front of you with the expected result, although you’d need to change your whole workflow to be 3D.
Thank you, I appreciate your taking time for this.
Actually, I find using a null-size raycast under the cursor very useful. It provides a lot of very easy to access information. It’s not a technique I’ve seen elsewhere but I guess there are better ways of doing it. I’ll certainly check out what you suggest, in particular Control nodes.
I realise that raycast can’t help with depth but I’m still looking for the tool that would mimic human perception. Even in a flatland world, there are still sprites “on top” of each other. I want to be able to pick that up, in the same way as a person playing a game would expect to be able to pick the sprite up with a drag gesture. It’s a sort of 3d world represented in 2D, much as playing cards are in many games.
Back to the drawing board but seriously - thank you again.
Beechside | 2020-01-14 22:55
Ah - thank you for adding the reference to
intersect_point. That was unknown to me - I haven’t delved into the low-level routines at all. I think a combination of that and sorting by z_order should do the trick.
Beechside | 2020-01-15 16:09
OK, thanks to Zylann, I got this working. It’s not elegant but it allows you to select the node under the cursor with the highest
z_index, regardless of the order of nodes in the tree. Posted here in case it has some merit (assumes nodes under the cursor are Area2D)
func select_piece() -> Area2D:
var pos: Vector2
var intersects: Array
var top_piece: Area2D
var top_z: = -1
pos = get_viewport().get_mouse_position()
intersects = get_world_2d().get_direct_space_state().intersect_point(
pos, 32, , 0x7FFFFFFF, true, true)
for piece in intersects:
if piece.collider.z_index > top_z:
top_piece = piece.collider
top_z = piece.collider.z_index