#Finding an empty position

48 messages · Page 1 of 1 (latest)

shy sluice
#

So I'm quite new to Godot, only been using it for three days, so I'm still very much in the learning phase. I'm trying to find an random position that doesn't collide with my StaticBody2D's in order to set it as a target position when my main scene loads but I can't figure out how to do it inside a loop. Here's the code I currently have, running inside the _ready function of my main scene node:

    var valid = false
    var pos
    while !valid:
        var x1 = $BG.global_position.x + 100
        var y1 = $BG.global_position.y + 100
        var x2 = x1 + $BG.size.x - 200
        var y2 = y1 + $BG.size.y - 200
        pos = Vector2(randi_range(x1, x2), randi_range(y1, y2))
        $target_tester.position = pos
        if !$target_tester.has_overlapping_bodies():
            valid = true

This doesn't work (as in, it always sets valid to true on the first loop regardless), I'm guessing because the physics collisions doesn't update correctly until the next physics frame, so the overlapping areas of my Area2D $target_tester and static bodies don't trigger during the loop.

What would be the Godot method for achieving this without having to iterate over multiple frames (it seems to me, depending on how busy the scene is, it could take quite a number of frames to find a free position without doing it inside a loop)?

limpid forge
#

Hello, have you checked the collision masks/layers?

shy sluice
#

Yeah, they are all set to 1

limpid forge
#

Your approach seems correct so I'm just kind of spitballing here, if you don't mind

Could you verify that $target_tester does not actually become null on runtime? (You should be able to see this in the debugger while game is running)

#

Actually I think it should give you an error with the $ notation, nevermind...

#

What is target_tester, an Area2D that is a child of which node?

shy sluice
#

it's a child of the main node, which is running the code i posted

#

so my idea that the physics processing was lagging a frame behind isn't correct?

limpid forge
#

You are probably right about that, reading the docs I see this:

For performance reasons (collisions are all processed at the same time) this list is modified once during the physics step, not immediately after objects are moved. Consider using signals instead.

#

One solution that might work without having to iterate over multiple frames or use signals...

#

Make the staticbody a rigidbody?

#

just, one that doesn't move...

shy sluice
#

i'll give it a go

limpid forge
#

Hmm actually I think you'll run into the same issue... even Rigidbodies update only once per frame

#

I'm not sure of how to proceed but I'll keep thinking on it. Hope someone more knowledgeable can help.

shy sluice
#

thanks for trying 🙂 i'll keep banging my head against it, lol

limpid forge
#

Are you able to get the StaticBody2D and the Area2D to collide at all?

#

Having issues even doing that on my test build lol

shy sluice
#

doing some testing now

#

as far as i can tell it collides with staticbodies fine if it's done in the _process() function

#

but i'm still confirming that it's not colliding with some other thing that isn't one of the static bodies i'm interested in lol

#

yeah, i've confirmed that it can collide in the _process with the bodies i'm interested in

#

even using a signal that gets emitted from target_tester when it overlaps with a body never triggers in the while loop

#

as in, if i reverse the logic so the loop only exits if it has collided with a body, an overlapping check in target_tester will never trigger, so it never emits the signal and the loop will never end

limpid forge
#

I confirmed that as well

#

So it seems like no matter what, if we want to use godot's built in collision system, we cannot use a while loop / do this all on one frame, because Godot only updates the list of overlapping bodies once per frame. And there does not seem to be a function to force-update this list during the physics step/frame.

We could simplify the shapes of the colliders into circles (1 vector point with a size) and rectangles (4 vector points), and detect using math, and get that all done in one frame. But we lose the built-in functionality of polygons or capsule-shapes, etc

#

This seems like a fairly common problem (find a nearby open space) so I am surprised our options here are so limited

shy sluice
#

i know, lol

#

it's kinda bizarre

#

i come from gamemaker, so this is what i'd need to do there:

   xx = irandom_range(0, width)
   yy = irandom_range(0, height)
}```
#

seems silly to have to jump through so many hoops just to check a position, but oh well

limpid forge
#

Right, Godot's collision system seems a bit inflexible in this regard

#

wish we could force that list update

shy sluice
#

yeah

#

i guess i'll just add all the bodies to a group and iterate through the group using intersecting shape maths

#

i would actually prefer to be able to check against the navigation region, rather than against the bodies directly

limpid forge
shy sluice
#

yeah, i actually already attempted that

#

it can only be done in the _physics_process

limpid forge
#

ah damn

shy sluice
#

in any case, thanks a lot for the help 🙂 you've sanity checked me enough that i feel confident doing a more thorough workaround

limpid forge
#

no worries, I learned a few things!

shy sluice
#

i just figured i must be missing something simple considering the problem

limpid forge
#

Guess it's still possible we're both missing something and some guy will pop in soon with "guys just do ___"

shy sluice
#

hahaha, i hope so

shy sluice
#
    var pos
    var valid = false
    var target_shape = CircleShape2D.new()
    target_shape.radius = 100
    while !valid:
        var x1 = $BG.global_position.x + 100
        var y1 = $BG.global_position.y + 100
        var x2 = x1 + $BG.size.x - 200
        var y2 = y1 + $BG.size.y - 200
        pos = Vector2(randi_range(x1, x2), randi_range(y1, y2))
        var space_state = get_world_2d().direct_space_state
        var param = PhysicsShapeQueryParameters2D.new()
        param.shape = target_shape
        param.transform.origin = pos
        var result = space_state.intersect_shape(param)
        if result.size() == 0:
            valid = true```
#

however, you're only able to access the space like this if you don't have the physics running on a separate thread, if you do, you'd have to use the method we discussed earlier