I'm building a collection of minigames in Godot, and I've run into a bit of a problem with the drag-and-drop mechanics. I've managed to implement this functionality into the game, however during testing I've discovered that when several objects are dragged one after the other very quickly, the previous object, which is not being currently dragged, may move with the one currently being dragged. I've tried to fix it by using call_deferred and such, but nothing seems to completely fix the issue.

Any advice would be appreciated! Perhaps there is a better way to implement the click-and-drag functionality? The draggable objects on the screen may overlap, which is why I'm using buttons instead of area2D, but maybe I'm wrong in doing that?

The Draggable hierarchy is:

  • Draggable (TextureButton)
  • - Area2D
  • - - CollisionShape2D

The code for the draggable object:

class_name Draggable extends TextureButton

@export var drop_kind : String = ""

var draggable : bool = false
var is_inside_droppable : bool = false

var body_ref = null
var body_entered = null
var body_exited = null

var offset : Vector2
var initial_position : Vector2
var tween : Tween

func _ready():
	initial_position = global_position

func _process(_delta):
	if draggable:
		if Input.is_action_just_pressed("click"):
			initial_position = global_position
			offset = get_global_mouse_position() - global_position
			GlobalDragging.currently_dragging = true
		
		if Input.is_action_pressed("click"):
			global_position = get_global_mouse_position() - offset
			
		elif Input.is_action_just_released("click"):
			GlobalDragging.currently_dragging = false
			
			if !is_inside_droppable:
				if tween:
					tween.kill()
				tween = get_tree().create_tween()
				tween.tween_property(self, "global_position", initial_position, 0.2).set_ease(Tween.EASE_OUT)

func _on_mouse_entered():
	if !GlobalDragging.currently_dragging:
		draggable = true
		scale = Vector2(1.05, 1.05)


func _on_mouse_exited():
	if !GlobalDragging.currently_dragging:
		draggable = false
		scale = Vector2(1.00, 1.00)
		
func on_drop_area_entered(body : Area2D):
	body_entered = body
	
	is_inside_droppable = true
	body_ref = body
	
func on_drop_area_exited(body : Area2D):
	body_exited = body
	
	is_inside_droppable = false

The code for the drop area:

class_name DropArea extends Area2D

@export var drop_kind : String = ""

func _ready():
	area_exited.connect(on_area_exited)
	area_entered.connect(on_area_entered)
	
	
func on_area_entered(area : Area2D):
	thumbs_up_emoji.call_deferred(area)
	
func on_area_exited(area : Area2D):
	if drop_kind == area.get_parent().drop_kind:
		area.get_parent().on_drop_area_exited(self)
		
func thumbs_up_emoji(area):
	if drop_kind == area.get_parent().drop_kind:
		area.get_parent().on_drop_area_entered(self)

    zero_secrets_unfound maybe move the drag function from _process() to _input() funtion?

    Because you are bogged by the process, but input (mouse) can run faster than process..

    I don't recommend to use this control feature as it quite sucks to be honest.

    D&D is not that hard to make using more generic Node2D or Sprite2D nodes, I have a sample to add to my useful chunks of code project, actually I should have add it for a while now, I guess I have to kick myself where it needs to add at least basic sample code.