godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.17k stars 98 forks source link

New pathfinding method for huge worlds (New method for SourceGeometryMode) #9800

Open JekSun97 opened 6 months ago

JekSun97 commented 6 months ago

Describe the project you are working on

Strategy

Describe the problem or limitation you are having in your project

The problem is that if we create a huge NavigationRegion2D polygon, the agents will start to have problems, the problem is described in more detail here - https://github.com/godotengine/godot/issues/92184

Describe the feature / enhancement and how it helps to overcome the problem or limitation

I came up with an interesting method that could solve my problem, it is a slightly different pathfinding method, and its accuracy is not always perfect, but this pathfinding is used in Game Maker, and for most cases it is ideal, it would be great if it was added as additional option for pathfinding.

I attached NavigationRegion2D to my character who should look for a path, now NavigationRegion2D is a descendant of CharacterBody2D, we draw a region polygon around the character, the accuracy of the path depends on this region, and if after some time we constantly baked this region in a new way, it could bypass obstacles. Dynamic baking is certainly bad for performance, but this region is not large, only around the character, which would not greatly impact performance, moreover, my test with 200 such characters, which have a region and constant mesh baking, did not show any decrease in performance.

The agent tries to move towards the goal, despite the fact that its territory is limited; when static objects enter a region, the region will bake them to bypass them.

https://github.com/godotengine/godot-proposals/assets/130399274/43fc6ef2-adf3-4ff3-b686-6bd68f5650f7

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

If NavigationRegion2D had a new Node mode in the SourceGeometryMode parameter, in it it would be possible to select a 2D node from the scene, which would store all the static objects that would participate in baking the region, then such a method could be used!

Such a pathfinding would look something like this:

https://github.com/godotengine/godot-proposals/assets/130399274/fd9b25b1-c684-460f-b964-7c0a3ac786b3

Such problems can occur if the region around the character is too small:

https://github.com/godotengine/godot-proposals/assets/130399274/d4f0c9fd-89ef-48be-8ef1-de0ef4b30796

If this enhancement will not be used often, can it be worked around with a few lines of script?

No. For this to work, you just need to add the Node method I described above to SourceGeometryMode, then the size of the world would not matter for finding the path.

Is there a reason why this should be core and not an add-on in the asset library?

It's useful to have a method like this for the default SourceGeometryMode.

smix8 commented 6 months ago

Your idea is in general heading in the right direction as you only want navigation mesh within a reasonable distance around your active actors when dealing with large worlds. Your proposed solution might even be a working solution for some projects.

Naturally I am part of the doomsayer-squad that sees all the technical constrains that exist in the current navigation system that may cause problems for this so bear with me.

You said you added 100 agents and performance was nearly the same which I believe.

The performance issues do not directly correlate with the agent count and more with the navigation map and navigation mesh complexity. You can have ten thousands of agents if your entire navigation map only has 1-2 polygons to search through. Things are very different in performance when you have ten thousands polygons to search through just for 1-2 agents.

On a static navigation map with the navigation mesh not changing things will stay performance stable but as soon as you change region or navigation mesh the navigation map needs to update its navigation mesh polygon soup and all the edge connections. Those navigation map updates get increasingly costly the larger and more complex the combined navigation mesh gets.

While this is a general performanc issue with navigation meshes, by moving a NavigationRegion2D node with your agent like proposed it triggers such a costly navigation map update on every single frame. On top every single agent connected to that map will query a new navigation path on the map change. Because your moved region is still freeform the entire geometry parse and rebake of the navigation mesh is also far more costly compared to a simple grid-based navigation that just flips a cell state. I think you can see why this proposed approach will ballon into a performance issues quickly with the current system.

In general it is always better to not move regions and instead toggle static region chunks around the active actors.

chunk

Keeping the region chunks in place avoids a good part of the heavy recalculations costs every frame that would happen when a region is moved. Also when regions stay fixed they can fit into a large world grid and this way can also more efficiently be rebaked for runtime changes. If regions are moved every frame arbitrary with agents all those things require very costly recalculations. That is why nearly all large open world games are done on static navigation mesh chunk systems.

JekSun97 commented 6 months ago

@smix8 It's a shame that NavigationRegion2D is so problematic =(. But I think that the new mode for SourceGeometryMode will not be superfluous, but will only be a pleasant addition for new experiments with path finding

We can group scenes more conveniently, where we will have one node with all the obstacles, and it will give new opportunities, for example, such as I described above :)

By the way, you have a great example on the GIF, can I get it somewhere?

smix8 commented 6 months ago

By the way, you have a great example on the GIF, can I get it somewhere?

I don't really have anything shareable, this just a small part of a dirty test project. Basically each chunk that you see is a NavigationRegion2D that uses the NavigationPolygon baking rect and the new border size properties from Godot 4.3 to rebake a navigation mesh for a specific world chunk. Then it is a matter of using a script that just remaps the player position to the chunk cell and enable the regions around it while disabling all other regions.

Part of the explanation for the chunk baking is already added to the documentation for the Godot 4.3 branch here