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
-
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.
-
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.
- To create a clickable object: This requires the group
clickableObject
, and 3 methods that are attainable through aCollisionShape2D
or otherwise- Mouse Motion signal
- Mouse exited signal
- Function when a click happens (not a signal)
- 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.
- Area2D (group = clickableObject)
- CollisionShape2D (pickable = true)
- RectangleShape2D (local to scene = on)
- TextureRect (mouse = ignore)
- CollisionShape2D (pickable = true)
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)
- 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)
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()
Not a problem. To be honest I'm not too familiar with Y-Sorting so I can't give you any expert solution to that problem.