Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Integrated the new NavigationServer and NavigationServer2D #34776

Merged
merged 2 commits into from
Feb 10, 2020

Conversation

AndreaCatania
Copy link
Contributor

@AndreaCatania AndreaCatania commented Jan 3, 2020

This is the integration of the NavigationServer proposed here: godotengine/godot-proposals#332

What's new

Retro compatibility

This new NavigationServer, that is used for both 3D and 2D part, even if it offers many new features it's fully retro compatible.

Map & Regions welding

The Navigation node now creates a new Map in the server, and is possible to add as many Regions you desire by simply adding NavigationMeshInstance under the Navigation node (perfectly equal as before). The difference is that the Regions now are welded all together to form one single fully navigable map.
This operation is cheap because it doesn't regenerate the NavigationMesh, rather it just connects the many (probably pre-baked) Regions. You can remove and add Regions as you like at runtime.

NavigationMesh baking

The NavigationMesh can be pre baked in the editor (as before), or can be baked during the game without stopping it. This operation is not cheap and for this reason it's done in a separate thread, once it's done, it automatically sets the new NavigationMesh without any stop. (See the below 3D gif)

NavigationAgent

Even if the old workflow is still valid, now it's possible to use the NavigationAgent node that simplifies a lot the map navigation.
The NavigationAgent just needs to know the desired final location; it internally takes care to compute the optimal path. This path may or may not change during the game, but what you have to only care about is to just reach the next location returned by: $NavigationAgent.get_next_location(), let the agent do the work for you.

Collision Avoidance

Since the NavigationMesh baking is an heavy operation, an algorithm for collision avoidance that take cares of Dynamic obstacles got introduced.
As mention before, you can compute the player velocity using the position returned by the get_next_location() function but this function doesn't take care of the dynamic obstacles.
To avoid collisions you have to set the calculated velocity to the agent, using: $NavigationAgent.set_velocity(velocity), then the event: _velocity_computed(safe_velocity): is emitted with the parameter safe_velocity that is a version of your velocity vector that avoids dynamic obstacles.

Obstacles

The collision avoidance takes care to avoid collision with other agents and obstacles. Agents and Obstacles are different only for the fact that Obstacles can't be directly controlled. Similar to the Agents, is possible to create an Obstacle by adding the node NavigationObstacle.
The NavigationObstacle doesn't have any parameter because it's automatically initialized and updated, so you have just to add it to the scene.

2D

The Navigation2DServer is a bridge to the NavigationServer that allow to use the latter using 2D classes, like Vector2D or Transform2D.
The nodes NavigationAgent2D and NavigationObstacle2D works exactly like the 3D version described above.

Multi thread safe

The NavigationServer keeps tracks of any call and executes these during the sync phase. This mean that you can request any change to your map using any thread without any worrying.

3D version

In the below GIF you can see a character that navigates the map, composed by two different regions, while avoiding collisions with dynamic objects. Most important, you can see that after a bit the right side region is modified and a call to bake allows me to Regenerate the navigation mesh and so reach the above part before not reachable.

Also note that the pink box is a kinematic object, while the black balls are rigid bodies.

ezgif com-video-to-gif(3)

In this GIF it's possible to see that moving a region, automatically executes the map re-link and immediately allows the navigation. This step is done in real time.

ezgif com-video-to-gif(4)

2D version

Similarly to the above 3D version, the character is navigating two regions while avoiding the dynamic obstacles collisions.

ezgif com-video-to-gif(2)

Example project

Navigation2.zip

This work has been kindly sponsored by IMVU.

@reduz
Copy link
Member

reduz commented Jan 3, 2020

Awesome work! Will throughly review the coming week

@Duroxxigar
Copy link
Contributor

@AndreaCatania I noticed in the previous PR that you had, you did document everything in the .xml files, but I'm not seeing it in this PR. Perhaps I overlooked it?

@ghost
Copy link

ghost commented Jan 4, 2020

Looking forward to this. Though I didn't make it too far in testing yet, the engine crashes. (Win10 64-bit 118aaa5)

In the 2D (Level3) example I copied the Rigidbody2Ds, since the interesting thing to me about RVO would be how lots of entities navigate and with different sizes and speeds.

image

godot.windows.tools.64.exe!std::_Default_allocator_traits<std::allocatorRVO::Plane >::constructRVO::Plane,RVO::Plane(std::allocatorRVO::Plane & __formal, RVO::Plane * const _Ptr, RVO::Plane && <_Args_0>) Line 761 C++
godot.windows.tools.64.exe!std::_Uninitialized_backout_al<std::allocatorRVO::Plane >::_Emplace_backRVO::Plane(RVO::Plane && <_Vals_0>) Line 1600 C++
godot.windows.tools.64.exe!std::_Uninitialized_move<RVO::Plane *,std::allocatorRVO::Plane >(RVO::Plane * const _First, RVO::Plane * const _Last, RVO::Plane * _Dest, std::allocatorRVO::Plane & _Al) Line 1699 C++
godot.windows.tools.64.exe!std::vector<RVO::Plane,std::allocatorRVO::Plane >::_Umove(RVO::Plane * _First, RVO::Plane * _Last, RVO::Plane * _Dest) Line 1559 C++
godot.windows.tools.64.exe!std::vector<RVO::Plane,std::allocatorRVO::Plane >::_Emplace_reallocate<RVO::Plane const &>(RVO::Plane * const _Whereptr, const RVO::Plane & <_Val_0>) Line 709 C++
godot.windows.tools.64.exe!std::vector<RVO::Plane,std::allocatorRVO::Plane >::emplace_back<RVO::Plane const &>(const RVO::Plane & <_Val_0>) Line 661 C++
godot.windows.tools.64.exe!std::vector<RVO::Plane,std::allocatorRVO::Plane >::push_back(const RVO::Plane & _Val) Line 671 C++
godot.windows.tools.64.exe!RVO::Agent::computeNewVelocity(float timeStep) Line 189 C++
godot.windows.tools.64.exe!NavMap::compute_single_step(unsigned int _index, RvoAgent * * agent) Line 577 C++
godot.windows.tools.64.exe!ThreadArrayProcessData<NavMap,RvoAgent * *>::process(unsigned int p_index) Line 50 C++
godot.windows.tools.64.exe!process_array_thread<ThreadArrayProcessData<NavMap,RvoAgent * *> >(void * ud) Line 64 C++
godot.windows.tools.64.exe!ThreadWindows::thread_callback(void * userdata) Line 55 C++
[External Code]

image

@AndreaCatania
Copy link
Contributor Author

@avencherus I've fixed a problem, I'm quite sure it was related. Can you please check it now? If it will continue to not work I'll test it in windows.

However, I've updated the sample project by adding more characters and other changes to allow it:

Navigation2.zip

2020-01-04 11-16-31

@AndreaCatania
Copy link
Contributor Author

@AndreaCatania I noticed in the previous PR that you had, you did document everything in the .xml files, but I'm not seeing it in this PR. Perhaps I overlooked it?

@Duroxxigar No, you are correct. That PR was based on the RVO only feature and that docs was written by @nathanmerrill; that documentation is no more valid, for this reason you can't find it here.

However, if someone want to document it, I'm available to help him.

@tengkuizdihar
Copy link

AT LAST!
Now I don't need to implement my own collision avoidance system in 2D by clipping the navigation polygon. Thanks bro.

@Zireael07
Copy link
Contributor

I'm more happy with the merge navigation meshes/nav polygons part - I was trying to achieve merging in GDScript and had to ultimately scrap that in favor of using A* for pathing.

@Duroxxigar
Copy link
Contributor

Duroxxigar commented Jan 4, 2020

@AndreaCatania Good to know. I've been eagerly looking forward to something like this for quite some time. Once I got some downtime this weekend, I was going to start working on the documentation myself. Most likely starting this evening (so in about 5-6 hours hopefully). I want this thing to be well understood.

I also want to point out that the engine crashes when I try to add a navigation agent node. I have the issue reported on your fork of Godot (I figured that would be the right way to handle that). This still happens with your most recent change as well.

@starry-abyss
Copy link
Contributor

Great work!
Does it by any chance support multiple layers of navigation maps? For example, one for ground agents, one for swimming agents, and one for flying agents.

///
/// Note: All the `set` functions are commands executed during the `sync` phase,
/// don't expect that a change is immediately propagated.
class NavigationServer : public Object {
Copy link
Member

@aaronfranke aaronfranke Jan 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a change slated for Godot 4.0, and 3D nodes will be renamed to have 3D suffixes, shouldn't this new system be called NavigationServer3D?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I saw that, but this is not yet a decision made nor we are in a transition state. So I thought that it's not yet optimal a name like: NavigationServer3D.
We could change idea in any moment and use another kind of naming strategy.

@ghost
Copy link

ghost commented Jan 5, 2020

@AndreaCatania Thanks. Gave it a retest this morning. The changes appear to have resolved the crash, and your example project works well. Though the agents are a bit jittery and sometimes become sluggish, is this expected, and to be resolved by the end implementation of the game?

Also I reran the project where I fiddle about and created the crash, and oddly the agents ignore the walls in many cases.

godot windows tools 64_2020-01-05_10-23-42
godot windows tools 64_2020-01-05_10-23-45

After some time, a few agents at the end of goal will restart their pathing.

Here is my project so you can inspect what I did. (This is from Level3)

RVO.zip

Copy link

@ghost ghost left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is to be merged, maybe update the copyright headers to 2020. :)

ClassDB::bind_method(D_METHOD("agent_set_radius", "agent", "radius"), &Navigation2DServer::agent_set_radius);
ClassDB::bind_method(D_METHOD("agent_set_max_speed", "agent", "max_speed"), &Navigation2DServer::agent_set_max_speed);
ClassDB::bind_method(D_METHOD("agent_set_velocity", "agent", "velocity"), &Navigation2DServer::agent_set_velocity);
ClassDB::bind_method(D_METHOD("agent_set_velocity_target", "agent", "target_velocity"), &Navigation2DServer::agent_set_target_velocity);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be either target_velocity or velocity_target for consistency. Same for the 3D variant. I like the latter more because it'd show up next to agent_set_velocity in the docs (although I'm not sure there is any naming conventions in use.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think "velocity target" is the least confusing. "Target velocity" sounds similar to "target's velocity" (velocity of some other guy besides the agent), while "velocity target" is more obviously the level of velocity to be reached.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, shouldn't it be set_agent_radius instead of agent_set_radius? I think there's a discussion about this specifically somewhere, and I can't find it, but #33026 is related.

Copy link
Contributor

@Zylann Zylann Jan 6, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I see such naming I think of the convention used in servers in general, as they are close to a C-like API, so because they use to take a RID as "self", the type of "self" would be prepended as the first word of the function's name. In that sense it's consistent with servers

@AndreaCatania
Copy link
Contributor Author

AndreaCatania commented Jan 7, 2020

Great work!
Does it by any chance support multiple layers of navigation maps? For example, one for ground agents, one for swimming agents, and one for flying agents.

@starry-abyss It's already possible. Each Navigation node is a map. You can have three maps one for the Walking area one for Swimming area, the last one for Flying area.

@AndreaCatania
Copy link
Contributor Author

@Naryosha Updated

@AndreaCatania
Copy link
Contributor Author

@avencherus

Though the agents are a bit jittery and sometimes become sluggish, is this expected, and to be resolved by the end implementation of the game?

Yes this is expected. You have to set the agent properties (like Time Horizon, Max Speed) properly so the Agent can decide to react earlier or later to an obstacle.

Also I reran the project where I fiddle about and created the crash, and oddly the agents ignore the walls in many cases.

This is expected since the walls doesn't have any collider.

After some time, a few agents at the end of goal will restart their pathing.

This is a feature. The Agent doesn't control the start nor the end of the path resolution, but this is up to the developer decide how to use it. In my case, I've decided to run the path resolution endless so even if the character reached the final destination it can still fight to be more near possible to the end.

ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");

ADD_SIGNAL(MethodInfo("navigation_mesh_changed"));
ADD_SIGNAL(MethodInfo("bake_done"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this signal be named bake_finished? I believe other signals throughout the engine have it in this way. I don't recall a signal with "done" for the name. (Only suggesting for constancy sake)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are other signals with the _done suffix, like:

  • _thumbnail_done
  • _tree_thumbnail_done
  • _file_list_thumbnail_done
  • _preview_done
  • _thread_done
  • _request_done

I didn't found any event with _finish suffix.
Despite this, do you think it would make more sense: bake_finish?

Copy link
Member

@Calinou Calinou Jan 8, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndreaCatania We have signals that end with completed and finished, so we should use past tense instead of present tense.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndreaCatania The first one that pops in my mind is with animations. I believe audio is "finished" as well. I wasn't aware of the other signals you listed. Other than the _request_done one, but that one slipped my mind.

@Calinou If this is the case - does this call in the question some of the ones that they mentioned?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My fault, in the fast check that I did this morning I searched for: _finish" instead of _finished".
I've already changed it, it's bake_finished now.

@TheSHEEEP
Copy link

TheSHEEEP commented Feb 11, 2020

Is there any plan for this to be backported to 3.2 in one of the LTS releases?

I'm asking since I'm almost done with version 0.1 of my own navigation module (focused on procedural generation, so no editor integration) for Godot 3.2, and now I am wondering if I should continue working on it beyond that.

https://github.com/TheSHEEEP/godotdetour

Though from what I've seen so far, both projects have very different foci and features. Eg. godotdetour supports multiple navmeshes at the same time from the get-go, as well as marking areas as water, grass, etc. and I might add other things as I need them.
And maybe most importantly, godotdetour uses detour (duh!) for everything, while NavigationServer uses RVO2 instead (not sure why, to be honest, is detour lacking something?).

@aaronfranke
Copy link
Member

aaronfranke commented Feb 11, 2020

@TheSHEEEP This would be very unlikely to be backported to 3.2, since it's a major change and has already broken compatibility (here). Generally, for something to be backported it would either be a useful or important bug fix, or an important feature that's very unlikely to cause regressions.

In a way, the "breaks compat" label is also a "backporting this would probably be bad" label.

@TheSHEEEP
Copy link

I thought so, just better to ask than to continue working on it if there's a significant risk it might just get obsolete the moment it is "done" ;)

Though I might have continued anyway due to the different foci of the libraries.

@bruvzg
Copy link
Member

bruvzg commented Feb 11, 2020

This PR breaks MinGW/clang cross build form macOS (I have not tested it on Linux or with gcc) with following error:

In file included from modules/gdnavigation/register_types.cpp:42:
In file included from modules/gdnavigation/navigation_mesh_editor_plugin.h:36:
In file included from ./editor/editor_node.h:34:
In file included from ./editor/editor_data.h:38:
./editor/plugins/script_editor_plugin.h:141:3: error: expected identifier
                FILE_OPEN,
                ^
.../llvm-mingw/x86_64-w64-mingw32/include/winnt.h:4900:43: note: expanded from macro 'FILE_OPEN'
#define FILE_OPEN                         0x00000001
                              

@owstetra
Copy link

i get this Error when Compiling on Windows with MinGW :

In file included from ./scene/3d/navigation.h:34,\n                 from ./mod
ules/gridmap/grid_map.h:34,\n                 from modules\\recast\\navigation_m
esh_generator.cpp:54:\n./scene/3d/navigation_mesh_instance.h:40:7: error: redefi
nition of 'class NavigationMeshInstance'\n class NavigationMeshInstance : public
 Spatial {\n       ^~~~~~~~~~~~~~~~~~~~~~\nIn file included from modules\\recast
\\navigation_mesh_generator.h:35,\n                 from modules\\recast\\naviga
tion_mesh_generator.cpp:31:\n./scene/3d/navigation_mesh.h:198:7: note: previous
definition of 'class NavigationMeshInstance'\n class NavigationMeshInstance : pu
blic Spatial {\n       ^~~~~~~~~~~~~~~~~~~~~~\n"
=====
scons: *** [modules\recast\navigation_mesh_generator.windows.tools.64.o] Error 1

scons: building terminated because of errors.

Sorry , i copy it directly from CMD

@akien-mga
Copy link
Member

And from Linux with MinGW/GCC :)

Compiling ==> thirdparty/rvo2/src/Agent.cpp
In file included from thirdparty/rvo2/src/Agent.h:40,
                 from thirdparty/rvo2/src/Agent.cpp:33:
thirdparty/rvo2/src/API.h:42:10: fatal error: SDKDDKVer.h: No such file or directory
 #include <SDKDDKVer.h>
          ^~~~~~~~~~~~~

@akien-mga
Copy link
Member

Why does Rvo2 rely on Windows SDK headers in the first place? That's bogus and should be fixed, the library should be cross-platform.

@bruvzg
Copy link
Member

bruvzg commented Feb 11, 2020

Nothing in the API.h seems to be required, it's probably only for using RVO2 as dynamic library, I have reduced it to #define RVO_API and it builds on both macOS and MinGW.

@crotron
Copy link

crotron commented May 15, 2021

Does this change solve #28235?

@aaronfranke
Copy link
Member

This has been backported to the 3.x branch in #48395 and will be in Godot 3.5 when it's released.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.