Clicking on top collider only

This worked - with what you were saying before about setting the ordering to true, thanks!!! - I should’ve been more thorough with my testing.

For the benefit of anyone else who was struggling with this here is the code that worked thanks to @Sauermann.

Working example

Both the stage and item objects are StaticBody2D with a Sprite2D and CollisionBody2D - the _on_input_event is signalled from the CollisionBody2D so the clicks match the shape.

(Edit) Oh also - don’t forget to set the objects as pickable under Input in the inspector!!

Code for Stage:

extends StaticBody2D

@export var item : PackedScene

func _ready():
	get_parent().get_viewport().set_physics_object_picking_sort(true)

func _on_input_event(_viewport, event, _shape_idx):
	if event is InputEventMouseButton and event.is_pressed():
		var new_item = item.instantiate()
		new_item.global_position = event.position 
		get_parent().add_child(new_item)

The get_parent().get_viewport().set_physics_object_picking_sort(true) was what was missing from every other post I’ve seen about this. Note that the docs state this is off by default as it can be expensive.

Code for item

extends StaticBody2D

#tracking state
var button_down : bool = false
var dragging : bool = false
#used to offset start position for mouse so item doesn't snap centre to mouse position
var pos_delta : Vector2 = Vector2(0,0)
#used to switch to drag mode if mouse is moved further than limit while button pressed
var drag_distance = 3
var mouse_start : Vector2 = Vector2(0,0)
#used to show clicks
@export var costumes : Array[CompressedTexture2D]
var costume_num = 0

func _physics_process(_delta):
	if dragging:
		drag()

func drag():
	global_position = get_global_mouse_position() + pos_delta

func _on_input_event(viewport, event, _shape_idx):
	viewport.set_input_as_handled() #items with lower z-index will then ignore
	if event is InputEventMouseButton:
		if event.is_pressed():
			button_down = true
			pos_delta = global_position - get_global_mouse_position()
			mouse_start = get_global_mouse_position()
		if event.is_released():
			if button_down and not dragging:
				costume_num = (costume_num + 1) % costumes.size()
				%Sprite2D.texture = costumes[costume_num]
			button_down = false
			dragging = false
	if event is InputEventMouseMotion:
		if button_down and !dragging:
			if get_global_mouse_position().distance_to(mouse_start) > drag_distance:
				dragging = true
2 Likes