# Godot Clickable Overlap Objects and Deselect ## Summary of Problem If you want to have objects in your game be clickable, one of the most obvious methods is using a Area2D > Texture + CollisionShape2D. When your mouse interacts with the CollisionShape2D it has mouse motion, mouse exited, and input events. > Note: Control nodes have restrictions, eat up all mouse events, and don't work well with deselecting by clicking outside of any objects 1. The biggest issue is when you click on overlapping objects it will signal both events asynchronously, so there is not an easy way to differentiate the top object being clicked. 2. Also if you want selectable objects, you may wish to know when you click outside of a clickable object to deselect the currently selected object. ![image](https://user-images.githubusercontent.com/10157328/141661920-68c33018-0b7d-4504-8374-50a68f96e60a.png) ## Solution 1. To create a clickable object: This requires the group `clickableObject`, and 3 methods that are attainable through a `CollisionShape2D` or otherwise 1. Mouse Motion signal 2. Mouse exited signal 3. Function when a click happens (not a signal) 2. Input being handled in the root script Every time the mouse enters a clickable object, it will update the current "hovered node" by calling the group, and checking which one is the top most of the group, and sending a click event to it. Note that in godot the top most object in the scene list is the bottom most node returned in `get_nodes_in_group()`. This method also supports having a "selected node", which you can deselect by clicking outside any non-clickable objects. ### Clickable Object (BoxObject.gd) - Area2D (group = clickableObject) - CollisionShape2D (pickable = true) - RectangleShape2D (local to scene = on) - TextureRect (mouse = ignore) ```gd extends Area2D func leftMouseClick(): get_tree().get_root().selectedElement = self print('this object is on the top, and clicked!') func _on_BoxObject_input_event(_viewport, event, _shape_idx): if event is InputEventMouseMotion: get_tree().get_root().setHoveredNode(self) func _on_BoxObject_mouse_exited(): get_tree().get_root().unsetHoveredNode(self) ``` ### Root scene node / script (Game.gd) - Node2D - TextureRect (make sure mouse events are ignore for any control nodes or textures since they both consume mouse clicks) - BoxObject1 (bottom most BoxObject) - BoxObject2 - BoxObject3 - BoxObject4 (top most BoxObject) - BlackBlob (another `clickableObject` group, same methods as BoxObject, top most node in the list) ```gd extends Node2D var hoveredElement = null var selectedElement = null func unsetHoveredNode(node): if self.hoveredElement == node: self.hoveredElement = null # Returns boolean if it is the new top hovered object func setHoveredNode(node1): if self.hoveredElement == null or self.hoveredElement == node1: self.hoveredElement = node1 return true var nodes = get_tree().get_nodes_in_group('clickableObject') var hoveredPosition = 0 var i = 0 for node in nodes: if node == self.hoveredElement: hoveredPosition = i break i += 1 var node1Position = 0 i = 0 for node in nodes: if node == node1: node1Position = i break i += 1 if node1Position >= hoveredPosition: self.hoveredElement = node1 return true else: return false func _input(event): if event is InputEventMouseButton && event.pressed: if event.button_index == BUTTON_LEFT: if self.hoveredElement == null: # No object is being clicked, so deselect selected node self.selectedElement = null else: # Call left click for only the top object that is being clicked self.hoveredElement.leftMouseClick() ``` ![image](https://user-images.githubusercontent.com/10157328/141662654-31e3fed3-b084-414f-aae8-c53e580224c7.png)