Collision Checks Showing True but not Behaving as Expected

Godot Version 4.3

SOLVED!

After many days (weeks?) I finally developed a working solution!

TLDR:
I built a system for picking up and dropping items, where you can pick up and drop multiple items at a time and multiple held items are stored in a stack. Everything worked perfectly EXCEPT for dropping multiple items, as they would fall in the same location as other items and effectively ‘disappear’ (items are snapped to a grid when put down.)

SOLUTION:
I created an array in a GlobalStack script (so a global array) that holds dropped items positions. Then, when you want to drop an item, it checks if the snapped mouse position is already stored in the array and, if so, returns false, which prevents you from dropping the item. When you pick up the item, the position in the array is removed.

This is really useful as it doesn’t use collision detection (which seemed too flawed for this for various reasons) and may offer other useful options for dealing with collisions.

ORIGINAL POST:

UPDATE:

If it aint broke, don’t fix it.

Well, this broke, so I have done a massive overhaul of how this system works - building it again from scratch - now using a global script which controls the stack system. In building a check to stop items being placed on the floor I believe I have discovered the issue.

When a collision check is made (_on_area_shape_entered or _on_area_shape_exited) it checks at the point of intersection, but doesn’t check if the two shapes are colliding constantly. As a result, the boolean variable ‘can_drop’ can be changed when exiting one collision shape even if it goes directly into another collision shape which should set it to false. A different collision check would have to be made to be constantly checking if the two areas are colliding, which I am not sure is achievable via signals. Any help in working a solution around this issues would be greaty appreciated!

Original Question

I am creating a system for my ‘Burger Joint’ game where you can pick up and put down multiple items at once. The system works almost flawlessly, with one exception.

You can pick up one or multiple items, and you can put down one item, but putting down multiple items is buggy/inconsistent.

The controls are fairly simple; clicking on an item picks it up, or puts it down if already picked up (and there is space available.) Holding down the mouse button picks up multiple items as you sweep the mouse across various items:

Patties 1
Patties 2
Patties 3

The issue arises when it comes to putting items back down. I have a collision check for if an item is already in the place where you want to put the item down (can_drop is a bool variable to check this and print( can_drop ) consistently returns true or false), however the items are quite often placed down even when the can_drop variable is false:
Code Snippet 1
I have tested this code in _process() function and in the _physics_process() function as I had read that sometimes running collision checks from the _process() function does not work properly, however the results are the same.

The only thing I can think of is that it is a grid snapping issue. When an item is put down, it snaps it’s location to a 24 x 12 pixel grid, so maybe it is checking the collision fine, but the grid snap coordinates are in a collision (so to speak). I am not convinced by this though, as sometimes it puts down two items at a time, which may be the real issue, however I cannot find the source of this issue.

To add a little more detail to the process, the item class - when clicked on - sends a signal to a stack_controller (a simple Node2D) that handles various functions, such as sorting through the stack when the mouse wheel is scrolled. It then feeds information back to the item through another signal. I add this as there are potentially multiple points where the ‘can_drop’ check can be done.

I am a beginner when it comes to GODOT (and coding in general). All my experience comes from Game Maker, however I would say I am reasonably good with programming in GODOT.

Many thanks in advance!

UPDATE:

Here is the code process for the ‘picking up and putting down’ system.

The item.gd (Area2D Node) script is as follows:

# Check if mouse is over instance
func _on_mouse_entered() -> void:
	mouse_over = true
func _on_mouse_exited() -> void:
	mouse_over = false

# Check if area is free to drop item
func _on_area_shape_entered(area_rid: RID, area: Area2D, area_shape_index: int, local_shape_index: int) -> void:
	can_drop = false
func _on_area_shape_exited(area_rid: RID, area: Area2D, area_shape_index: int, local_shape_index: int) -> void:
	can_drop = true

# Pick up and put down item (signal receiver from stack_controller)
func pick_up_and_put_down_item( id, pick_up, new_stack_position ) -> void:
	if id == get_instance_id():
		if pick_up:
			grabbed = pick_up
			myself.set_collision_layer_value( 1, false )
			stack_position = new_stack_position
		elif can_drop:
			grabbed = pick_up
			myself.set_collision_layer_value( 1, true )
			stack_position = -1
			position.x = ( round( get_global_mouse_position().x / 24 ) * 24 )
			position.y = ( round( get_global_mouse_position().y / 12 ) * 12 )
	# Adjust stack position of other items in stack
	elif not pick_up:
		if stack_position > 0: stack_position -= 1

# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta: float) -> void:
	
	# Delete item (temporary function!)
	if Input.is_action_just_pressed("Delete"): queue_free()
	
	# Follow cursor if item is grabbed
	if grabbed:
		position = get_global_mouse_position()
		if stack_position > 0:
			position.x += 12
			position.y -= ( stack_position * 2 )
	
	# Pick up and put down item (signal emitter to stack_controller)
	if Input.is_action_pressed( "mouse_left_click" ) and not is_busy:
		if mouse_over and can_drop and not grabbed_trigger:
			if not grabbed:
				grabbed_trigger = true
				var id = get_instance_id()
				item_clicked_on.emit( id, stack_position )
			else:
				grabbed_trigger = true
				var id = get_instance_id()
				item_clicked_on.emit( id, stack_position )
	
	# Reset grabbed_trigger
	if Input.is_action_just_released( "mouse_left_click" ):
		grabbed_trigger = false

And the stack_controller.gd (Node2D) script is:

# Organise items in stack
func item_sort( id, stack_position ) -> void:
	
	# Check if item is not currently grabbed
	if stack_position == -1:
		mouse_stack.append( id )
		var stack_size = mouse_stack.size()
		stack_size -= 1
		alter_item_state.emit( id, true, stack_size )
	
	# Check if item is already grabbed
	else:
		alter_item_state.emit( id, false, -1 )
		mouse_stack.remove_at(0)

The idea is that the item, when clicked on, sends a signal (with its id) to a stack controller node (Node2D) which assigns that id to an array. This is used to organise stack_position of the items (where they are in the array is their position within the stack.)
This may be better achieved by placing the array within a global variable, but this is not relevant to the current question and I do not think this would solve my current issue.

I hope this helps better understand the system and helps with diagnosing my problem.

I realise this is quite long, so thank you for reading this far!

Can you share you project on GitHub? It would be pretty hard to find a proper cause why this happens without seeing the whole scene setup.

I will look into this as have never used GitHub before. In the meantime I’ll add an update to show the whole process of picking up and putting down items. I may also have an better/simpler method for this process so this may still change yet, my apologies in advance.

This isn’t your issue, but this piece of code is redundant. You can eliminate the if/else check since the results of both are the same.

if Input.is_action_pressed( "mouse_left_click" ) and not is_busy:
		if mouse_over and can_drop and not grabbed_trigger:
			if not grabbed:
				grabbed_trigger = true
				var id = get_instance_id()
				item_clicked_on.emit( id, stack_position )
			else:
				grabbed_trigger = true
				var id = get_instance_id()
				item_clicked_on.emit( id, stack_position )
2 Likes

Ah I didn’t see that! I believe it once was useful but in tinkering around it has now become redundant. Thank you for pointing this out.