How to prevent a wide Area2D from overlapping walls when instanced?

Godot Version

4.4

Question

Hi everyone! I’m a beginner in programming and game development, and I’m working on a puzzle platformer where you use a slingshot to shoot seeds. Wherever the seed lands, a special plant grows — and you use these plants to progress through the level

The problem is: some of the plants (like a big sunflower trampoline) are quite wide, and when I shoot a seed too close to a wall, part of the plant ends up overlapping or clipping into it. I’m instancing the plant as an Area2D with a CollisionShape2D, and I’ve tried several solutions — including raycasts and checking collisions — but I just can’t get it to work properly

how can I adjust its position automatically so it fits entirely on the platform without intersecting walls?

Thanks in advance (and sorry if this is an obvious question)

Lets figure out why the collision does not happen or is not accounted for.

Plant is Area2D. Properties “Monitoring” and “Monitorable” are checked is assumed. And you have function for both signals “area_entered” and body_entered".
What type are the walls? I just guess RigidBody2D here. “Contact Monitoring” and a signal function is applied.
Both “Plant” and “Wall” have the same collision layer and mask ticked. Then add some breakpoints or print statements to the collision signal functions. This way we can make sure if the engine recognises the collisions.

If all above does not help, try moving the plant (Area2D) one pixel in any direction after add_child(). And see if this triggers the collision signal functions. Reason: There seems to be a weirdness that RigidBody and StaticBody does not trigger Area2D collision if the Area2D does not move:

You’d need to detect if the Plant is overlapping the Wall and move it away from the wall.
Here’s my rough implementation of the solution, let me know if that works for you.
This is the code that would go to your Plant scene. And you need to add all your walls to a “Wall” group, or change the first lines to recognize a wall in a different way.

class_name Plant
extends Area2D


var is_repositioning: bool


func _ready() -> void:
	body_shape_entered.connect(_on_body_shape_entered)


func _on_body_shape_entered(_body_rid: RID, body: Node2D, body_shape_index: int, local_shape_index: int) -> void:
	# check if colliding body is a wall, in this case I used groups, but you can also check it any other way
	if not body.is_in_group("Wall"):
		return
	
	# check if the plant is already being repositioned, so that it doesn't loop when colliding with more than 1 wall
	if is_repositioning:
		return
	is_repositioning = true
	
	# assign shapes to check collisions
	var body_shape_owner_id: int = body.shape_find_owner(body_shape_index)
	var body_shape_owner: CollisionShape2D = body.shape_owner_get_owner(body_shape_owner_id)
	var body_shape: Shape2D = body.shape_owner_get_shape(body_shape_owner_id, body_shape_index)
	var local_shape_owner_id: int = shape_find_owner(local_shape_index)
	var local_shape_owner: CollisionShape2D = shape_owner_get_owner(local_shape_owner_id)
	var local_shape: Shape2D = shape_owner_get_shape(local_shape_owner_id, local_shape_index)
	
	# check which direction to move the plant
	var direction_x: float = body_shape_owner.global_position.x - local_shape_owner.global_position.x
	
	# move the plant by 1 pixel, until it doesn't collide with the wall anymore
	while local_shape.collide(local_shape_owner.global_transform, body_shape, body_shape_owner.global_transform):
		if direction_x < 0:
			global_position.x += 1
		else:
			global_position.x -= 1
	
	is_repositioning = false

Quick demo:

Thank you so much for helping me!

The wall is actually a tileset. so I had to do some changes, but your code helped me a lot!

1 Like