Help finding nearest area without collisions

Sky LionSky Lion Posts: 7Member
in 2D

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?

Comments

  • TwistedTwiglegTwistedTwigleg Posts: 2,125Admin

    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!

  • Sky LionSky Lion Posts: 7Member
    edited March 9

    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!

  • TwistedTwiglegTwistedTwigleg Posts: 2,125Admin

    @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.

  • Sky LionSky Lion Posts: 7Member
    edited March 10

    @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!

  • TwistedTwiglegTwistedTwigleg Posts: 2,125Admin

    Great! I'm glad you were able to get it working!

Leave a Comment

BoldItalicStrikethroughOrdered listUnordered list
Emoji
Image
Align leftAlign centerAlign rightToggle HTML viewToggle full pageToggle lights
Drop image/file