Best way to determine a mouse click that doesn't intersect with any physics objects [2D]

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

If it were me designing the engine, I would have made _unhandled_input() happen AFTER the camera casts a ray, but that’s not how things operate in godot: InputEvent — Godot Engine (3.1) documentation in English

For example, in an RTS you could have a click on a unit select it, but a click on the “ground” be used for dragging the map. I could track all unhandled clicks and all physics object clicks and only pan the camera if I didn’t select a unit, but that seems hacky. I can put an area/shape attached to the camera. I use get_tree().set_input_as_handled()but objects in the foreground don’t block the background from getting input, it seems.

Is there a preferred/better way to go about this?

I don’t know the answer to your question, but in your example, I would expect the camera to pan even if I started the drag event on a unit: Press and release without dragging (= Click): Select unit (if applicable) - Press, drag and release: Pan the camera without selecting any unit. For this to work, it would not be relevant for the background event handler whether the same event was received by any other object before: Just set the game state to “drag mode” once the mouse is dragged with a pressed button, and let units only react to the release event when not in drag mode.

Thomas Karcher | 2019-05-31 06:16

:bust_in_silhouette: Reply From: Zylann

I don’t know how you are doing your pciking, but you can easily raycast to the ground by casting a ray with a collision mask which only matches the ground (snippet from my project):

func _physics_process():

	var mpos = get_viewport().get_mouse_position()
	var ray_pos = _camera.project_ray_origin(mpos)
	var ray_dir = _camera.project_ray_normal(mpos)
	var space_state = get_world().direct_space_state
	var ground_only = # your mask here
	var hit = space_state.intersect_ray(ray_pos, ray_pos + ray_dir * 200.0, [], ground_only)
	if not hit.empty():
		# Do stuff with the result

Then you could cache the result if you want to use it elsewhere.