Currently our Mass avoidance solution can lead to soldiers getting outside the NavMesh bounds. Detour Crowd makes sure to avoid obstacles while staying within NavMesh, but it's designed only to work with actors. Also Mass avoidance leads to entities colliding into each other when they shouldn't.
TODO:
[x] Figure out how this will work in multiplayer with replication. Currently we replicate move target actions and then interpolate on client to move entity between waypoints. If instead the server is calculating velocity for entities and we send that to client there's potential for mismatch of entity location on client and server. Also we'd need some client-side prediction/rollback which would require reimplementing parts of the CharacterMovementComponent. Also that approach would mean a lot more network traffic. Some alternatives could be (1) have detour crowd logic run on client and server and server will only tell client the destination or the waypoints (2) keep move target logic and enhance avoidance processors to consider NavMesh boundaries.
[x] Complete action items from notes below
[x] Could subclass UE’s traits such as movement and based on console variable do superclass behavior or our custom behavior without adding move target. This will make it easy to toggle between the two as it's in development.
[x] Delete snap to nav mesh processor?
[x] Ideally create at least one functional test for this if not a few to prevent future regressions. One common issue we previously had was that entities would end up off NavMesh during NavMesh move so would be nice to have a test for that.
UCrowdManager uses dtCrowd to update UCrowdFollowingComponent in UCrowdManager::Tick.
dtCrowd takes in a RecastMesh, which you can get from ANavigationData, see how UCrowdManager::CreateCrowdManager calls dtCrowd::init
We could create our own version of UCrowdManager called something like UMassCrowdMovementProcessor. Instead of updating UCrowdFollowingComponent it would update Mass fragments.
Ultimately dtCrowd calculates what velocity each agent should be. See UCrowdManager::ApplyVelocity for how it gets dtCrowd's calculated velocity and sets it on UCrowdFollowingComponent. Based on UseAccelerationForPathFollowing it may take the velocity and apply it as an acceleration instead.
Note this Detour Crowd Manager system uses a O(N) algorithm where N is number of active agents. It can be seen because UCrowdManager::Tick calls dtCrowd::updateStepAvoidance which for each active agent will call sampleVelocity (which calls dtObstacleAvoidanceQuery::processSample), which loops over m_ncircles which is max m_maxCircles (which is set from UCrowdManager.MaxAvoidedAgents) which should be a small constant. dtObstacleAvoidanceQuery::addCircle checks for m_maxCircles. Also getNeighbours has hard-coded 32 maximum.
Note for a large number of entities we'd probably need to make functions like dtCrowd::updateStepAvoidance run with ParallelFor. But it's not thread-safe as m_obstacleQuery is a singleton. We might be able to create a m_obstacleQuery for each thread.
For updating Detour with a NavMeshPath, see UCrowdManager::SetAgentMovePath, which gets called by UCrowdFollowingComponent::SetMoveSegment.
We'd need to ensure players are added as obstacles but not simulated by the system. To do so, ensure FCrowdAgentData.bIsSimulated is set to false. For an example see how UCrowdManager::AddAgent sets bIsSimulated.
Currently our Mass avoidance solution can lead to soldiers getting outside the NavMesh bounds. Detour Crowd makes sure to avoid obstacles while staying within NavMesh, but it's designed only to work with actors. Also Mass avoidance leads to entities colliding into each other when they shouldn't.
TODO:
Notes: