Detour Avoidance Groups

Does the Detour system use the same avoidance groups as RVO? How do we go about manipulating and tweaking Detour Avoidance. The characters do not seem to go around each other very effectively at the moment.

Still looking for an update to this. Is there a way adjust things like the radius the units take around each other or what units to avoid and which to not?

Sorry for late reply. AvoidanceGroup, GroupsToAvoid and GroupsToIgnore should work exactly the same as in simple RVO and agents that aren’t matching flags won’t be added as neighbors (no avoidance or separation).

Params to tweak in UCrowdFollowingComponent:

  • CollisionQueryRange : scan radius for neighbors
  • SeparationWeight + bEnableSeparation : additional force to keep agents apart (flocking)
  • AvoidanceQuality : number of avoidance samples

There’s also a debug drawing mode, although it’s not easily accessible: you need to change values of flags in CrowdDebugDrawing namespace (CrowdManager.cpp) and select an actor in Play/Simulate in Editor session. bDrawDebugVelocityObstacles should be most useful for testing avoidance.

thanks this is exactly the information I was looking for. The debugger was fairly simple to enable as soon as I setup a source build.

Looking into the SeparationWeight +bEnableSeparation, I’m a little confused as to it’s use. It’s a weight that is sent to the AgentData in the CrowdManager but I can’t track down where it’s actually used. I’ve tried jumping this up to 20 but the agents still run into each other quite often (which is strange) so how does it work?

It’s being used in dtCrowd::updateStepSteering function. If I remember correctly, velocity offset for neighbor in range is equal to weight * (1 - sqr(distance)), so actual values will depend on agent velocities. Try much higher ones, 25% of their speed should give visible repulsion.

So I’ve tried this and am having a bit of an issue with the avoidance groups. I have 2 groups set up. 1 for ground units and 1 for flying units. The collision profiles have them ignore each other but the avoidance has them slow down and stagger.

So, I set up avoidance groups based on team and unit type. the first digit is team and the second digit is unit type (or bCanFly) The result is something like Team on flying units being AG 11 and team 1 ground units being AG 10.

Team 2 would be 21 for flying and 20 for ground etc.

I then set up the avoidance groups units to avoid to be the same as the avoidance group they belong to while having each other one unchecked.

The problem is that the avoidance groups still seem to be registering the other units as neighbors. (see image) Should this be the case or do I have to mark off all other groups in the groups to ignore section?

Most flexible approach will be assigning an attribute to each bit in mask. Only downside is need of using ignore mask to achieve required filtering:

  • bit0 : am I team A?
  • bit1 : am I team B?
  • bit2 : am I ground unit?
  • bit3 : am I flying unit?

This will give us:

  • team A ground: group = (bGroup0, bGroup2), avoid mask = (bGroup0), ignore mask = (bGroup3)
  • team A flying: group = (bGroup0, bGroup3), avoid mask = (bGroup0), ignore mask = (bGroup2)
  • team B ground: group = (bGroup1, bGroup2), avoid mask = (bGroup1), ignore mask = (bGroup3)
  • team B flying: group = (bGroup1, bGroup3), avoid mask = (bGroup1), ignore mask = (bGroup2)

For small / not growing scope, you can assign each variant to separate bit and don’t bother with using two masks:

  • team A ground: group = (bGroup0), avoid mask = (bGroup0)
  • team A flying: group = (bGroup1), avoid mask = (bGroup1)
  • team B ground: group = (bGroup2), avoid mask = (bGroup2)
  • team B flying: group = (bGroup3), avoid mask = (bGroup3)

Just make sure that you’re using FNavAvoidanceMask as bit masks and don’t set flags directly through Packed member unless it’s something abvious like 0 of 0xffffffff for debugging sake.

Yea I had the first option set up and assigned it directly on the blueprint of my character since the crowd following component is EditAnywhere but got the same results.

/** Group mask for this agent */
	UPROPERTY(Category = "Avoidance", EditAnywhere, BlueprintReadOnly, AdvancedDisplay)
	FNavAvoidanceMask AvoidanceGroup;

	/** Will avoid other agents if they are in one of specified groups */
	UPROPERTY(Category = "Avoidance", EditAnywhere, BlueprintReadOnly, AdvancedDisplay)
	FNavAvoidanceMask GroupsToAvoid;

	/** Will NOT avoid other agents if they are in one of specified groups, higher priority than GroupsToAvoid */
	UPROPERTY(Category = "Avoidance", EditAnywhere, BlueprintReadOnly, AdvancedDisplay)
	FNavAvoidanceMask GroupsToIgnore;

What do you mean do not set the flags directly through packed member? Should I not be setting this directly on the blueprint?

Blueprints don’t have it exposed, I was just trying to cover all possibilities (blueprints & c++).

If it still doesn’t work (with both avoid and ignore masks) then I’m not sure what’s happening. Can you spawn just one unit of each type - everyone ignore each other, order them to move so crowd avoidance kicks in and debug getNeighbours() function in DetourCrowd.cpp ? It should skip over everything and return 0.

Hi Lukasz,

Last Friday I was pulled away to work on a different project but I’m back now. Anyway, when I said I set it in the blueprint I meant that it was changed on the defaults. I was not sure if the RVO avoidance groups doubled up for this but I though maybe it did not (since it’s on the movement component) so I set them directly using c++ now in the PostInitializeComponents.

I did run the test that you suggested and got the same result. Here is what I did.

I spawned 2 units next to each other. They have the following settings:

Unit 1: //Team1 Ground Unit

AvoidanceGroup: bGroup0 = 1, bGroup1 =0, bGroup2=1, bGroup3=0
GroupsToAvoid: bGroup0 = 1, bGroup1 =0, bGroup2=1, bGroup3=0
GroupsToIgnore: bGroup0 = 0, bGroup1 =1, bGroup2=0, bGroup3=1

Unit 2
//Team 1 Air Unit

AvoidanceGroup: bGroup0 = 1, bGroup1 =0, bGroup2=0, bGroup3=1
GroupsToAvoid: bGroup0 = 1, bGroup1 =0, bGroup2=0, bGroup3=1
GroupsToIgnore: bGroup0 = 0, bGroup1 =1, bGroup2=1, bGroup3=0

When I debug it Line 222 of DetourCrowd.cpp has the following check:

const bool bDontAvoid = (skip->params.groupsToIgnore & ag->params.avoidanceGroup) || !(skip->params.groupsToAvoid & ag->params.avoidanceGroup);

neither of these return a true so n always increments by 1.

Maybe It’s in the way I’m setting my groups. In my setup crowd avoidance function I setup 3 nav avoidance masks:

			FNavAvoidanceMask *group = new FNavAvoidanceMask();
			FNavAvoidanceMask *avoid = new FNavAvoidanceMask();
			FNavAvoidanceMask *ignore = new FNavAvoidanceMask();

then run the checks as we described earlier:

if (Team % 2 == 0)	
				{ 
					group->bGroup0 = 1; //We are team 0  
					group->bGroup1 = 0;	//We are not team 1
					avoid->bGroup0 = 1;
					avoid->bGroup1 = 0;
					ignore->bGroup0 = 0; //Do not ignore Team 0 for pathfinding
					ignore->bGroup1 = 1; //Ignore the enemy team so we can approach easily
				}
				else
				{
					group->bGroup0 = 0; //We are not team 0  
					group->bGroup1 = 1;	//We are team 1
					avoid->bGroup0 = 0;
					avoid->bGroup1 = 1;
					ignore->bGroup0 = 1; //Ignore the enemy team so we can approach easily
					ignore->bGroup1 = 0; //Do not ignore Team 1 for pathfinding
				}

				//check UnitSubtype
				if (UnitData.UnitSubtype == EShadowUnitSubtype::Air)
				{
					group->bGroup2 = 0;	//We are not a ground unit
					group->bGroup3 = 1;	//we are a flying unit
					avoid->bGroup0 = 0; //do not avoid other ground units
					avoid->bGroup3 = 1; //avoid other air units
					ignore->bGroup0 = 1; //Ignore ground units
					ignore->bGroup1 = 0; //Do not ignore air units
				}
				else
				{
					group->bGroup2 = 1;	//We are a ground unit
					group->bGroup3 = 0;	//we are not a flying unit
					avoid->bGroup2 = 1; //avoid other ground units
					avoid->bGroup3 = 0; //do not avoid other air units
					ignore->bGroup2 = 0; //Do not ignore ground units
					ignore->bGroup3 = 1; //ignore air units
				}

Finally I plug them into my crowd follow component by calling a setter I created for each:

Cast<USHCrowdFollowingComponent>(Cast<AShadowHeroesUnitAIController>(this->Controller)->PathFollowingComponent)->setAvoidanceGroups(*group);
			Cast<USHCrowdFollowingComponent>(Cast<AShadowHeroesUnitAIController>(this->Controller)->PathFollowingComponent)->setGroupsToaAvoid(*avoid);
			Cast<USHCrowdFollowingComponent>(Cast<AShadowHeroesUnitAIController>(this->Controller)->PathFollowingComponent)->setGroupsToIgnore(*ignore);

Hm… Everything looks ok, however it’s clearly not being passed to crowd agents since they still have problems with ignoring others.

Can you add another line after all group setters have been called?

Cast<USHCrowdFollowingComponent>(Cast<AShadowHeroesUnitAIController>(this->Controller)->PathFollowingComponent)->UpdateCrowdAgentParams();

It should refresh agent params in detour crowd simulation.

I added the UpdateCrowdAgentParams function call but still no dice. They still register other units.

So, I’ve tried to repo this in a separate project and was not able to. That leads me to believe it is some kind of interference coming from somewhere else. (Though I’m not sure where)

At first I thought it might be because I was overriding the crowd manager so I could increase the max number of agents. This required me adding my own custom navigation system who’s only difference is creating a MyCrowdManager instead of a CrowdManager. So I added that to the project as well. It still worked though.

So, I’m going to keep digging. Eventually I’ll find something that breaks it. My other question Here seems to work perfectly fine in a fresh project as well. So I’m guessing it might be something related. Either way, I just wanted tocheck in. This one really is a head scratcher.

1 Like

Well, my only suggestion right now is to make sure that agent is having correct params after you’ve set groups and hope that memory breakpoint triggers when those params are being modified.

Good luck! :slight_smile: