Hierarchical drag and drop

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

I’m trying to implement a 2d system where players would be able to drag and drop objects individually, while they’re laying on top of a larger “canvas” or “table”.

I’d like to implement it so that, if you click on a “draggable” object, that would follow the mouse until you drop it (release the button), but if you click on the underneath “table” you would drag the whole thing, as if you were moving the entire group (table and objects lying on it).

I first implemented single object drag and drop as such:

extends Node2D

var offset     = Vector2.ZERO
var start_pos  = Vector2.ZERO
var is_clicked = false
var is_dragged = false 

func _on_area_2d_input_event(viewport, event, shape_idx):
	if Input.is_action_just_pressed("click"):
		is_clicked = true
		is_dragged = true
	if Input.is_action_just_released("click"):
		is_clicked = false
		is_dragged = false
		
func _process(delta):
	if is_dragged:
		var mouse_pos = get_global_mouse_position()
		if is_clicked:
			start_pos  = global_position
			offset     = mouse_pos - start_pos
			is_clicked = false
		global_position = mouse_pos - offset

(I know, I should lerp by delta, but at the moment I was just trying to implement the basics :slight_smile: )

I started by organising my main scene like this:

_Main
|_ Background
|_ Draggable

Where the background is my “table” and the draggable object is what lays on top of the table.
If I instantiate them in isolation, it all works (I can drag background on its own or draggable on its own), so I thought it would be just a matter of parenting one to the other and make both visible.

As soon as they’re both enabled, the drag and drop behaviour would only work for one of them, even if I click on the other.

Is there a way to specify the order of operations, so that the input event would be registered based on the drawing order, or something like that?

:bust_in_silhouette: Reply From: exuin

I couldn’t find anything on what order CollisionShape2D receives input events, so I don’t think there’s a way to do it.

A hacky way would be to use Controls instead of Areas, since they will always receive events from child to parent.

I couldn’t find anything on what order CollisionShape2D receives input
events, so I don’t think there’s a way to do it.

Interesting…
should this be a feature request?

A hacky way would be to use Controls instead of Areas, since they will
always receive events from child to parent.

I can try that.
Would this mean th earea 2d event in here:

func _on_area_2d_input_event

would be instead connected to a Control node, thus becoming:

func _on_control_input_event

I haven’t used controls yet, not sure what they imply.

emilianop | 2023-02-28 21:06

it would be _gui_input()

exuin | 2023-02-28 21:17

Thanks, will give it a go!

emilianop | 2023-03-01 22:56

This doesn’t seem to work.
I have my “draggable” scene organised as:

_ Draggable (Node2d)
|_ Control (Control) 
|_  Icon (Sprite2d)

Control is connected to the Draggable node and the gd script goes:

func _on_control_gui_input(event):
    if event is InputEventMouseButton and event.is_pressed():
        print("pressed")

I almost never get a registered pressed signal, 99% of the times my output window is empty.

I’m not even checking event.button_index == MOUSE_BUTTON_LEFT, doesn’t seem to make any difference.

emilianop | 2023-03-02 09:36

Oh… rookie mistake, should have checked the size of my control… that was too small to catch all the inputs.
After framing it to the size of the icon I can get expected behaviour.

I’ll try your suggestion of using nested controls now.

emilianop | 2023-03-02 09:58

I can confirm it works!

I can now drag the draggable and it will follow the mouse AND drag the background and they will both follow the mouse.

Thank you

emilianop | 2023-03-02 10:04