• 2D
  • Help finding nearest area without collisions

Hi all! I'm a web dev who's new to game dev and Godot. I'm currently working on my first game, and I could use some help.

I need help figuring out how to do the following:

To spawn an area (a rectangular Area2D of a predefined size) centered on a target position. If there are collisions where it would spawn, to instead spawn it (or move it) the least distance from that target position to where there are no collisions.

Any ideas?

I have not tested or really done too much code like this, but right off I think you can do something like this:

extends Area2D

var check_for_position = true
var rect_size = Vector2(32, 48) # example size.

func _physics_process(_delta):
	if (check_for_position == true):
		var bodies_in_area = get_overlapping_bodies()
		if (bodies_in_area.size() > 0):
			move_out_of_range(bodies_in_area)
		else:
			check_for_position = false

func move_out_of_range(bodies):
	var current_position = global_position
	for body in bodies:
		var direction_from_body_to_self = (current_position- body.global_position)
		# check distance on the X
		if (abs(direction_from_body_to_self.x) < rect_size.x):
			var distance_to_add += (rect_size.x - abs(direction_from_body_to_self.x))
			distance_to_add = distance_to_add * sign(direction_from_body_to_self.x)
			current_position.x += distance_to_add
		# check distance on the Y
		if (abs(direction_from_body_to_self.y) < rect_size.y):
			var distance_to_add += (rect_size.y - abs(direction_from_body_to_self.y))
			distance_to_add = distance_to_add * sign(direction_from_body_to_self.y)
			current_position.y += distance_to_add
	global_position = current_position

That is how I would try to solve the issue, though it probably needs adjusting. One issue with the code is that it doesn't account for when there is no possible solution, nor does it account for infinite loops where the Area2D keeps getting pushed back and forth between several collision shapes. Another issue with the code above, is that it really doesn't take advantage of Godot's physics system to help accomplish moving the Area2D out of collisions. I'm not totally sure how it would work, but I'm guessing there may be a way to use the physics in Godot to accomplish this task.

Regardless, hopefully this helps a bit. Also, welcome to the forums!

This is tremendously helpful. Thank you so much for taking the time to consider my issue and write a solution in code. I've implemented this with good success!

However an issue remains: when the obstructing collisions are TileMaps (the borders of a level), this fails, since the returned position for them is (0, 0). I've been trying to find out how to get the global position of a TileMap that's colliding with an Area2D without success. Would you happen to know?

And thanks for your warm welcome to the forums!

@"Sky Lion" said: This is tremendously helpful. Thank you so much for taking the time to consider my issue and write a solution in code. I've implemented this with good success!

Awesome! :smile:

However an issue remains: when the obstructing collisions are TileMaps (the borders of a level), this fails, since the returned position for them is (0, 0). I've been trying to find out how to get the global position of a TileMap that's colliding with an Area2D without success. Would you happen to know?

Hmm, well, the reason it is returning (0, 0) is because it is getting the global position of the node and not the actual collision point. This, unfortunately, means that TileMaps will not work without adjusting, because all of the tiles with collision are part of the TileMap, whose position does not reflect each individual tile.

You might be able to get the exact collision point using a the collide_and_get_contacts function, as detailed in this Godot Q&A answer. However, this requires knowing the collision shape of the other object, which may not be retrievable from a tilemap.

Another option is to raycast around the edges of the Area2D rectangle, and then adjust according to the raycast results. It would be more performance heavy, but it would work around the issue.

@TwistedTwigleg , thank you so much. I finally got it working!

I tried so many things (spiral sampling, physics, area bodies, etc.), and the least buggy by far was your initial solution. Due to the issues with TileMaps, however, I took your suggestion to try out ray-casts, and with them I was able to implement the feature perfectly!