#Navigation2D - avoidance with several nav maps

96 messages · Page 1 of 1 (latest)

molten iris
#

What is the best/most efficient way to do 2D navigation? With a region and having the tilemap with obstacles as a child? Or with tilemap navigation layers? (In the case of tilemap navigation layers I'm not even sure how to go about it since my tilemap is layered, with objects like trees having ground tiles under them).
Also, another question: how do you avoid enemies getting stuck on corners?

#

(I'm also considering the option of making my own system, if it's really that bad currently with Godot's, but I'm not sure if I have to or if it's too overkill)

winter gyro
#

avoiding the TileMap is usually the answer.

#

to not have enemies stuck on collision you need a margin between navmesh surface and collision shape. The TileMap can not create or bake such a margin, you need e.g. a NavigationRegion2D for that.

molten iris
#

well, even keeping the radius at a 10 on the navigation polygon, enemies still get quite stuck on corners of the navigation polygon

#

it's not that they stop entirely, but they slow down a lot to the point that it's pretty much the same as getting stuck in terms of behavior

winter gyro
#

that depends on what else is going on, getting stuck is usually a physics problem, as long as you have valid navmesh layouts you can not get stuck at corners because the paths can only ever be on the navmesh. It is usually the physics velocity drag or the path desired distance skipping of the NavigationAgent2D that allows agents to leave the navmesh path and that is what gets them stuck.

molten iris
#

gonna record it to show what I mean, give me a second

#

some random assets to test things. and the tilemap is put as a child of the navigation region. with an agents radius of 10px. you can see how much the enemy slows down on corners. in some situations it almost entirely stops though here it only slowed down for a little bit

#

a radius of 10px is already a pretty big issue if I have smaller objects with collision, such as those rocks or trees, since having 2 close together would basically make it impossible for the enemy to go through there despite fitting

winter gyro
#

well a navmesh is the surface for an agents center, if an agent can not stand there with its center there should be no navmesh, everything else is a physics shape problem. That also looks like the typical physics slowdown that you get when the shapes collide.

#

but I dont see the navmesh or collision in that video, but the path looks fine.

molten iris
#

here's how the region looks

winter gyro
#

navmesh looks fine, might be even too much margin, but that depends on what collision shape you are using for those rocks / trees / houses.

molten iris
#

alright, figured it out

molten iris
#

a radius of 10px is too much for those

#

buuut

#

I think I figured it out after debugging more stuff visually

#

had to lower these

#

thats what was going on

winter gyro
#

path desired distance allows corner skipping if set too high yes

#

because it is the distance at which the agent will go to the next path point

#

and if that is a corner it might leave navmesh

molten iris
#

the enemies were hugging the collision because they were allowed to stick a little further away from their path, thats pretty much it

#

still sucks that I have to have a radius this big to avoid wall/corner hugging

#

not sure if anything can be done

winter gyro
#

you might just need to look out that your agents do not get stuck on navmesh edges if the desired distance is too small. That can happen should you update the path often or if you agent moves too quick. the agent might overshoot path points and backtrack.

molten iris
#

managed to lower the radius to 6 px instead of 10 and it's still good enough. gonna guess that it's sadly collision shape dependent, aka bigger enemies will still have issues?

winter gyro
#

you might have to end up what many games do and have different collision shapes for your actors movement collision and your "hitbox" collisions.

molten iris
#

so basically way smaller collision shapes despite enemy size in order to avoid issues like these

winter gyro
#

many games have a smaller and simple circle / sphere collision shape for world geometry collision so the agent has more options to manoeuvre without getting stuck easily.

#

and a dedicated collision shape just for hit detection or collision with other actors.

molten iris
#

it'd be much better if there was a way for me to make bigger enemies walk further away from the obstacles, but I don't see how that'd be doable with this system

winter gyro
#

well that is the navmesh bake radius, larger agents shrink the navmesh and that solves the issue as far as pathfinding is concerned, it does not account for all the other possible shenanigans with movement like physics velocities and shapes because agents are just points in a navigation system.

molten iris
#

what I meant is that, for example, if an enemy would be twice the size of this one...obviously it'd get stuck on corners again

#

unless i gave it a really small collision shape

#

which might make it look awkward since it could fit through spaces it shouldn't

winter gyro
#

for each distinct agent size it requires its own navigation map with a navmesh baked for those agent properties.

molten iris
#

and wouldnt it be a lot more laggy if I had several navigation regions?

#

(I'd probs not need more than 2-3 but just curious)

winter gyro
#

not nessarily because they each do their own thing, the only thing is that it requires extra memory for the navmesh graph but there is not really much of another way to have multiple different agent sizes as a navmesh is a tailor made surface for a specific agent type. Think of it like a traffic map, pedestrians, bikes, cars, trucks, while maybe using the same "geometry" still all need their own navigation map because they can not necessarily share the same or it is not efficient to do so.

#

most games that have different agent sizes usually have 2-3 different navigation maps

#

games like Horizon Zero Dawn used 7 navigation maps for example just to give you an idea how common that is

molten iris
#

I see. yeah, that's perfect then. 2-3 is plenty for me

#

how would I set each agent to a different nav region though?

#

with the nav layers?

winter gyro
#

that requires using the NavigationServer2D directly

#

you can still setup NavigationRegion2D nodes but you need a script and switch them to a different map, same for the agents.

#

by default they all use the default navigation map of the Viewport World2D

molten iris
#

are you sure? it seems to work just fine with 2 nav regions on different nav layers (and the agents assigned to the respective layer)

#

assigned the bigger enemy and the nav region with the bigger radius to layer 2 and they seem to work

#

or is this not what you meant?

winter gyro
#

navmesh surfaces can not overlap, that will give you edge merge errors and logic bugs, and the only way to separate them is by moving them to different maps. if it still works in your case that is more by luck. the moment edges overlap things might explode already.

molten iris
#

so they should be assigned to different navigation maps which can only be created by code?

#

with something like this? then assigning the new animation map to the nav region and agents

winter gyro
#

yes, create the map and set it active, then set all the NavigationRegion2D.set_navigation_map() to that map, same for the agents.

molten iris
#

other than that, everything else is fine? such as having those two navigation regions set up that way like this?

winter gyro
#

yeah you can do with the NavigationRegion2D nodes what you want when you set them to a different map once, they will update the navmesh on their assigned map, it is just the editor debug that will rendered that convoluted mess of overlapping navmesh but on the NavigationServer2D they will be strictly separated.

molten iris
#

👍 got it

#

that's about it then, since I'm guessing there's no better way to do it in the editor itself

#

thanks!

molten iris
#

@winter gyro sorry to come back to this and ping but, if I do use your solution (have now moved to different maps instead of navigation layers), how do I make it so that enemies avoid each other again??

winter gyro
#

you mean with the avoidance? that is a little more difficult because currently the avoidance is based per map. So you would need to use scripts to create agents just for the avoidance only and assign them all to the same map just for the avoidance simulation. so you end up having 2 agents per real agent, one for pathfinding, one for avoidance.

#

the navigation documentation has a agent script example that shows how to create a server agent and avoidance callback. basically you create the agent, set the map, set the agent radius, and register a callable for the safe velocity callback, then each physics process you set the velocity of the agent and you get the ssafe velocity on the callback and use that to move your agent.

molten iris
#

oh boy

winter gyro
#

it sounds more difficult to desc it than what it actually is

#

it is just that there is no user interface for it

molten iris
#

I think I might stick to just adding a slight randomized offset for the target ngl

#

and are you positively sure it's an issue to use navigation layers for the regions? cause it seemed to limit the larger enemies to the larger radius and smaller enemies to the smaller radius

winter gyro
#

navigation_layers are just a bitmask filter for what polygons an agent can use in a path query

#

they cant help you with avoiding merge conflicts of navmehs polygons on the navigation map

#
var agent: RID = NavigationServer2D.agent_create()
NavigationServer2D.agent_set_map(avoidance_map_rid)
NavigationServer2D.agent_set_radius(agent_radius)
NavigationServer2D.agent_set_avoidance_enabled(agent, true)
NavigationServer2D.agent_set_avoidance_callback(agent, Callable(self, "_avoidance_done"))


func _physics_process(delta):
    var new_velocity: Vector2 = ... # calc velocity like with NavigationAgent2D parent
    NavigationServer2D.agent_set_velocity(agent, new_velocity)


func _avoidance_done(safe_velocity: Vector2):
    # move agent node like with NavigationAgent2D parent```
#

that is how you create and use a server agent for avoidance

molten iris
#

what mostly confuses me is how it works. because I still need to have the agent stuff that I have right now alongside the avoidance. would I just send the velocity I currently calculate to that agent instead for the safe velocity and everything else stays the same?

winter gyro
#

that is because the NavigationAgent2D has both combined

#

in theory you do not need it for the pathfinding, you could just NavigationServer.query_path() and use that path array to move your agent along.

#

but since you cant give the NavigationAgent2D a different map for pathfinding and a different map for avoidance you need to split it in that case, no other way if you want to use a different map. It is a little complicated because it was never designed with such a use in mind.

#

and yes, if you have a NavigationAgent2D in use with avoidance right now you would basically send the same velocity

#

because internally when you set NavigationAgent2D.set_velocity() it does the same for you, just calls NavigationServer2D.agent_set_velocity()

molten iris
#

I'm sorry, I tried and couldn't get it to work, but that's because I still don't get it. am I supposed to use the current navigation agents I had for pathfinding and the avoidance agent made through the server for avoidance? I tried to do that but the enemies don't move properly at all. I tried setting everything just in case. didn't know which map to use or if I should make a new one (or how it'd be related to the 2 regions I had for the different enemy sizes) so I reused the big one. then the callback function. which is written identically to how I had it for the navigation agents beforehand. lastly, I just commented the code that set the velocity to the navigation agent and instead set it for the avoidance one. (sorry if this is slightly unclear, have a state machine structure there, but it's definitely not at fault here). oh and I also disabled the avoidance on the navigation agents cause I thought it'd interfere with this

#

Navigation2D - avoidance with several nav maps

molten iris
#

Checking everything, it seems like the velocity is correct before it calculates the safe velocity. It does enter that safe velocity function as well. And I made a new map for avoidance only, just in case, but nothing changed. Should any of the regions be assigned the avoidance map?

winter gyro
#

hey, no regions have nothing to do with it, the entire navmesh system has nothing to do with it, avoidance is its own simulation but that simulation is tied to a navigation map, so you can create a new empty map that is just for the avoidance of all the agents. The only thing that matters for avoidance is, map, agent position, radius, velocity (and other avoidance related properties, not listing them all but you already have them in your script) and the callback so you actually get the calculated velocity result.

molten iris
#

Then I dont really get why it didnt work right...

molten iris
#

it seems to be a consistent issue outside of my state machine code, as I tried setting up 2 character bodies with their navigation split between the navigation agent and avoidance agent, same thing happens: safe velocity's y is always 0

winter gyro
#

I am not sure why the safe velocity would be 0 just on one axis only. What Godot version are you using?

#

oh wait maybe cause in the older versions the NavigationServer2D used the NavigationServer3D behind the scene and the actual velocity was a Vector3. Not sure if that was converted on the server or on the node level.

#

can you try the avodance callback function with a Vector2 and with a Vector3 parameter? what does it print or are there errors?

#

because recently the NavigationServer2D was moved to its own server so it is a little confusing atm.

#

just looked at the source code and it stills sends a Vector3 with the callback that gets only converted at the NavigationAgent2D node level, so that is likely the reason.

molten iris
molten iris
#

yuuuup. thats it. x and z have values, y doesn't