Prevent mouse click from instantiating a new object if that same type of object is already in that location?

Godot Version

4.2.2

Question

I am trying to prevent a mouse click from instantiating a new object (a carrot) if that same type of object is already in that location.

I created $Area2D/WorldCollisionShape2D object that sticks to the mouse pointer. Then tried to use the _on_area_2d_area_entered(area: Area2D) and _on_area_2d_area_exited(area: Area2D) function to set a on_a_carrot variable to true or false, that did not work.

When I create carrot1, then move the mouse over and create carrot2, it will let me create carrot3 on top of carrot1.

How can i test the area under a mouse click and see if there are any WorldCollisionShape2D objects there?

Here is a screenshot of the carrot structure
carrot

Here is screenshot of the world structure
world

here is my world code:

extends Node2D

var carrot = preload("res://carrot.tscn")

var on_a_carrot = false

func _process(delta: float) -> void:
	
	#tiny collisionarea that sticks to mouse
	$Area2D/WorldCollisionShape2D.global_position = get_local_mouse_position()

	if Input.is_action_just_released("LeftClick"):
		Input.get
		if on_a_carrot: # exit if already on a carrot
			return #exit
		var carrotinstance : Node2D = carrot.instantiate()
		add_child(carrotinstance)
		carrotinstance.position = get_global_mouse_position()

func _on_area_2d_area_entered(area: Area2D) -> void:
	print("on a carrot")
	on_a_carrot = true

func _on_area_2d_area_exited(area: Area2D) -> void:
	print("off a carrot")
	on_a_carrot = false

Here is my carrot code:

extends Node2D

var on_a_carrot = false

# Called when the node enters the scene tree for the first time.
func _ready() -> void:
	if on_a_carrot == true:
		return
	$AnimatedSprite2D.play("seed")
	await get_tree().create_timer(1).timeout
	$AnimatedSprite2D.play("growlevel1")
	await get_tree().create_timer(2).timeout
	$AnimatedSprite2D.play("growlevel2")
	await get_tree().create_timer(2).timeout
	$AnimatedSprite2D.play("growlevel3")
	await get_tree().create_timer(3).timeout
	$AnimatedSprite2D.play("growlevel4")
	await get_tree().create_timer(4).timeout
	$AnimatedSprite2D.play("growlevel5")
	await get_tree().create_timer(5).timeout
	queue_free() #deletes the carrot

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	pass

func _on_area_2d_mouse_entered() -> void:
	if InputEvent:
		on_a_carrot = true

func _on_area_2d_mouse_exited() -> void:
	on_a_carrot = false

Here is the project on Github:

Any Pointers or Links would be great.

Enable Debug > Visible Collision Shapes and run your code.

You will notice in some cases when carrot colliders areas are close the mouse collider overlaps both. So on they way in over carrot A, it is also leaving carrot B. You should resize according to your needs. Also not sure if you need to check anything in carrot for colliding.

It’s easier to see if you disable queue_free()

1 Like

Thanks for the quick reply!

I enabled debug shapes and removed the queue_free().

It is as you said.
it works fine of the carrot collision areas are not overlapped.

if they overlap, the strategy of setting a variable based on _on_area_2d_area_entered(area: Area2D) and _on_area_2d_area_exited(area: Area2D) does not work.

Hmm, Trying to think of a solution…
Is there a way to make a List of all the carrot collision areas instances and then check to see of the new click was placed in an area in that List?

Screenshots:

  • screenshot of 2 separate piles of clicks, working as expected:
    2PilesofSeparateClicksWorkingCorrectly

  • screenshot of the issue occurring on when carrot collision areas overlap:
    2PilesOverLappingClicks

  • screenshot of trying bigger mouse collision area, but the issue re-occurred:
    TriedAbiggerMouseCollisionArea

updated files on gIthub:

Try this change in your World. Notice the has_overlapping_areas() method. Not sure if it’s enough in your final project, but definitely tells you if an area overlaps. If you need to know if it’s a carrot though you may want to use Array[Area2D] get_overlapping_areas() const check the Area2D docs or something more sophisticated or use layers.

func _process(delta: float) -> void:
	
	#tiny collisionarea that sticks to mouse
	$Area2D/WorldCollisionShape2D.global_position = get_local_mouse_position()

	if Input.is_action_just_released("LeftClick"):
		Input.get
		if $Area2D.has_overlapping_areas():
			return
		#if on_a_carrot: # exit if already on a carrot
			#return #exit
		var carrotinstance : Node2D = carrot.instantiate()
		add_child(carrotinstance)
		carrotinstance.position = get_global_mouse_position()
1 Like

That worked perfectly! Thank you! :grin:

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.