Unable to get NavArea Flags in SetMoveSegment() using custom PathFollowingComponent derived from UCrowdFollowingComponent

Hey guys,

So I’ve been following the tutorial shown HERE and have run into a wall while trying to get this working with the UCrowdFollowingComponent. (Parent)

Whenever I do a MoveTo(), SetMoveSegment() only contains a SegmentStart with NULL flags so I can’t determine whether we are traversing a ‘jump’ segment or not using the method shown in the tutorial.

Is there another way to determine if the current Segment we are about to traverse is a specific type of NavLinkProxy, or a possible way of making the UCrowdFollowingComponent correctly use the SetMoveSegment() function?

Through testing I have found if IsCrowdSimulationEnabled() is false then SetMoveSegment() works as expected (As a majority of the component is disabled), however if we simply SuspendCrowdSteering() It still fails to work correctly.

I guess I’m looking for point in the right direction/explanation of why this doesn’t work.

Cheers,

So after about a week of screwing around I’ve come across a solution which is hacky at best.

I found the code in the CrowdManager which was supposed to be detecting smartlinks was never working, as the following line in void UCrowdManager::UpdateAgentPaths() was always returning a NavLinkId of 0:

const uint32 NavLinkId = PImplNavMesh->GetLinkUserId(AnimInfo.polyRef);

So I ended up finding that the valid NavLinkId was offset by one in the Detour NavMesh connection data so long as we tick the ‘Smart Link is relevant’ box when placing a NavLink.

I ended up making the following change in DetourNavMesh.cpp so we fetch that NavLinkId instead of the invalid one.

See below:

const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) const
{
...
	const unsigned int idx =  ip - tile->header->offMeshBase;
	dtAssert(idx < (unsigned int)tile->header->offMeshConCount);
#if 1 // UG_CHANGE: Rather hacky way to guarantee that we correctly fetch the correct UserID for the custom link on this NavMesh.
	// As far as I can tell, the Custom Link ID is ALWAYS at the next index if the Smart Link is relevant.
	// So double check it is actually the case, and then return it.
	if (tile->offMeshCons[idx].userId == 0 &&
		idx + 1 < (unsigned int)tile->header->offMeshConCount &&
		tile->offMeshCons[idx + 1].userId != 0)
	{
		return &tile->offMeshCons[idx + 1];
	}
#endif // UG_CHANGE:
	return &tile->offMeshCons[idx];
}

I don’t have a great grasp of how the Detour NavMesh actually works fully however, so I’m not sure this is ACTUALLY what I should be doing, and if this works in all cases.

This is really only a tentative answer until I can get an answer from someone who actually knows the system. (Not me!)

Should mention that this doesn’t work with dynamically spawned NavLinks.

Posted a bug about it HERE.

So after much screwing around I finally figured out I’ve been mostly chasing my tail getting this to work, as it’s rather poorly documented/explained.

So to elaborate on my final solution:

  1. UCrowdFollowingComponent does not use SetMoveSegment() in the same way as UPathFollowingComponent, so you will need to use StartUsingCustomLink() instead. I ended up calling PauseMove(), setting the CharacterMovement to MOVE_Flying, and then manually lerping the characters position to DestPoint in the TickComponent(). Once it finished I set the movement mode back to MOVE_Walking and called ResumeMove()
  2. When placing NavLinkProxy actors in the level make sure the ‘PointLinks’ array under the ‘Simple Link’ Category is empty. Make sure you check on ‘Smart Link Is Relevant’. Simple links have a higher priority in the Nav System, so if you have both on, StartUsingCustomLink() will never get called. (Which isn’t explained anywhere…)
  3. If you want to place NavLinkProxy actors in the level at runtime; after you spawn the actor, you need to call: NavLinkProxy->GetSmartLinkComp()->ForceNavigationRelevancy(true) which will actually get the NavLink to actually appear. If that doesn’t work you may also need to call NavSys->AddDirtyArea(NavLinkProxy->GetComponentsBoundingBox(), ENavigationDirtyFlag::All); so the NavMesh around the link rebuilds.

And that should be about it. :slight_smile: