[SOLVED, WITH SOLUTION] How to click only the top Area2D?

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

I love Godot for the most part but it has a few ridiculous niggles, one of which I’ve encountered recently and cannot fathom the existence of nor the seeming lack of reasonable methods of navigation: the fact that all overlapping collision shapes in 2D will receive an input event for the mouse click instead of only the topmost one blocking the rest.

This issue is particularly prevalent in the isometric project I’m working on now as it necessitates overlapping collisions. I am wondering if anyone has some simple workarounds (rather than writing my own entire input queue which is ridiculous and turns me off the engine)?

:bust_in_silhouette: Reply From: gamedevshirious

Use collision_mask and/or pickable property

Hope it helps :slight_smile:

Unless I’m mistaken, neither of those features let’s me have overlapping collision shapes with only the top receiving moues clicks though?

Oscar | 2020-12-14 00:43

:bust_in_silhouette: Reply From: Oscar

Ok so after a little more messing around (including figuring out how to make intersect points work properly) I have figured out a universal workaround. If you create a new script (I called mine ‘Clicker’) with the following code and add it as an autoload singleton, you can then add the function on_click() to any Area2D you want and it’ll just work!

extends Node2D


var click_all = false
var ignore_unclickable = true


func _input(event):
	if event is InputEventMouseButton and event.pressed and event.button_index == 1: # Left mouse click
		var shapes = get_world_2d().direct_space_state.intersect_point(get_global_mouse_position(), 32, [], 0x7FFFFFFF, true, true) # The last 'true' enables Area2D intersections, previous four values are all defaults
		
		for shape in shapes:
			if shape["collider"].has_method("on_click"):
				shape["collider"].on_click()
				
				if !click_all and ignore_unclickable:
					break # Thus clicks only the topmost clickable
			
			if !click_all and !ignore_unclickable:
				break # Thus stops on the first shape

This snippet helped me a lot, a hundred thanks Oscar !

Tyrfang | 2021-11-24 17:31

No worries! Note though that, from memory, the way Godot returns intersecting objects is chronologically, so this system technically selects the newest object beneath the cursor, not the topmost. I’m considering trying to add a manual sort by z-index but it looks to be difficult and I ended up switching my input setup to get around it currently.

Oscar | 2021-11-24 23:24