I’m in the process of creating an inventory system with drag-n-drop functionality and I’m struggling to get picking up items to behave as I want. I want to be able to pickup the item by dragging with click+hold and place on release, and also to click to pick up and click again to place. I’ve tried every combination of structure but can’t figure out the one to get the desired behavior.
This code drags items and places items perfectly. It just can’t differentiate between a click and a drag, so clicking will pick an item up and place is the moment you release LMB.
var picked_slot: int = 0 # Indicates slot index. 0 = no item/slot picked from
var picked_item = [item_name, item_stack, can_stack]
var focused_slot: int = 0 # Indicates slot index. 0 = no item/slot is focused
var focused_item = [item_name, item_stack, can_stack]
func _input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.is_pressed():
if picked_slot > 0:
return
elif focused_slot > 0 and focused_slot <= inv_size:
pick_from_slot(focused_item)
is_dragging = true
elif event.is_released():
if is_dragging:
if focused_slot_has_item:
swap_items(picked_item, focused_item)
is_dragging = false
else:
place_into_slot(picked_item, focused_slot)
is_dragging = false
else:
if picked_item == 0 and focused_slot_has_item:
pick_from_slot(focused_item)
use a Control for this and override the _get_drag_data(), _drop_data() and _can_drop_data() with _set_drag_preview() to show what your are dragging or display some animation at the cursor.
I don’t know, I really tried to make it work using those earlier but I couldn’t get it to drop the data or work with my slots properly. I just don’t understand them enough to use them effectively.
I did however figure out a way to get it working but it might be kinda jank. I basically just time the click with an incrementing float. If click_timer < click_threshold, it’s considered a click and a drag if greater than.
var click_timer = 0.0
@export var click_threshold: float = 0.2
func _process(delta: float) -> void:
if is_dragging: click_timer += 1 * delta # Increments while pressing
else: click_timer = 0.0 # Reset when not pressing
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.is_pressed():
if focused_slot > 0 and focused_slot <= inv_size:
if picked_slot == 0 and !is_dragging and focused_slot_has_item:
pick_from_slot(focused_item) # Pickup regardless of dragging or click event.
is_dragging = true
elif event.is_released():
if is_dragging:
if click_timer < click_threshold: # Considered a click
is_dragging = false
return
else: # Considered a drag
if focused_slot_has_item: swap_items(picked_item, focused_item)
else: place_into_slot(picked_item, focused_slot)
is_dragging = false
That was a lot simpler and much clearer to implement, thank you! For future users, here’s a very basic example of how you could check the distance between events:
var is_dragging := false
var initial_pos := Vector2.ZERO
func _input(event: InputEvent) -> void:
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT:
if event.is_pressed():
initial_pos = event.position
elif event.is_released():
var distance = event.position.distance_to(initial_pos)
if distance < 10: # Click
is_dragging = false
print("Click")
else: # Drag
print("Dragging")
is_dragging = true