Hello! I have a collision problem. I have a packed scene with an Area2D and a square collisionshape2d.
I can drag these around the game window but if they are on top I don’t want the bottom zindex layer to detect the mouse event. Is there a option I am missing that prevents the area2d from going to the Area2D behind it?
I’ve fixed a similar issue in one of my projects but it was so convoluted.
It was done to prevent selection of 2 areas when overlapping.
Here it is fully working:
Here it goes.
Enjoy a good reading!
extends Node
class_name InteractionHandler
@onready var _selected_objects_list :Array[InteractableZone] = []
# This is a reference to the last object in the array.
@onready var _last_selected_object_in_list :InteractableZone :
get:
if ( _selected_objects_list == null or _selected_objects_list.is_empty() ):
push_error("Selected object list is null or empty.")
return
# Dunno why I've done it this way but there it is... If it works it works you know...
var _last_object :InteractableZone = _selected_objects_list[ _selected_objects_list.size()-1 ]
return _last_object
# I connect signals to funcions; nothing weird here.
func _ready() -> void:
PlayerEvent.interactable_zone_selected.connect( _on_selected_object )
PlayerEvent.interactable_zone_deselected.connect( _on_deselected_object )
pass
# Basically when an Area2D (InteractableZone) is entered/exited by mouse
# it emits a signal, passing "self" as an argument, which then is received here.
func _on_selected_object( new_selected_zone :InteractableZone ) -> void:
# It takes the zone as an argument and 'true' marks it as selected
# making the marker and the speech bubble appear.
_set_zone_as_selected( new_selected_zone, true )
## Adds the zone to the "selection list"
_append_object_to_array( _selected_objects_list, new_selected_zone )
## This fixes an issue where more than one InteractableZone could be selected at the same time.
for zone :InteractableZone in _selected_objects_list:
if not (zone == new_selected_zone):
_set_zone_as_selected( zone, false )
pass
# Same concept as above but the other way around.
func _on_deselected_object( deselected_zone :InteractableZone ) -> void:
_set_zone_as_selected( deselected_zone, false )
_erase_object_from_array( _selected_objects_list, deselected_zone )
if _selected_objects_list.is_empty():
return
# This fixes issues where the overlapping InteractableZone would not be selected automatically when exiting.
# Maybe because Interaction or Mouse Movement outside the area.
if _last_selected_object_in_list.can_interact():
_set_zone_as_selected( _last_selected_object_in_list, true )
CurrentGridsOver is an array that is populated when the mouse hover is detected. GetZIndex() is just a method on the top node that just returns the .z_index. AllowMouseHover is the bool that prevents logic being ran on the child nodes.
func CurrentOverGrid():
var highestZIndex = 0;
var grid;
if CurrentGridsOver.size() > 0:
for contain in CurrentGridsOver:
if contain.ContainerNode.GetZIndex() >= highestZIndex:
highestZIndex = contain.ContainerNode.GetZIndex();
grid = contain
grid.ContainerNode.AllowMouseHover = true
DebugUI.UpdateCurrentGridOverByZIndex(grid.ContainerGUID + ": " + str(grid.GridIndex))
for contain in CurrentGridsOver:
if contain.ContainerGUID == grid.ContainerGUID && contain.GridIndex == grid.GridIndex:
var r = 1
else:
contain.ContainerNode.AllowMouseHover = false;
if grid == null:
DebugUI.UpdateCurrentGridOverByZIndex("NONE")
return grid;