Teams
Every agent carries one team id - a plain integer you assign whatever meaning you like.
Agents spawn on team 0. Teams do two things: they filter queries, so you can ask for
"the enemy units in this rectangle" in one call, and they override avoidance between
specific pairs of teams.
Nothing about teams is required. Leave every agent on team 0 and the simulation behaves
exactly as it did before teams existed.
Setting and reading
Crowd->SetAgentsTeam(Squad, 1); // whole selection
Crowd->SetAgentTeam(Index, 2); // one agent
TArray<int32> Teams = Crowd->GetAgentTeams({}); // parallel to GetAgentPositions
Team ids can be any integer, including negatives. They are yours - the simulation never assigns meaning to a particular number, and changing an agent's team at runtime takes effect on the next query or tick.
Filtering queries
Every selection query takes a Teams array. Pass an empty array to get every agent,
or a list of team ids to keep only agents on those teams:
TArray<int32> Everyone = Crowd->GetAgents({}); // all agents
TArray<int32> Enemies = Crowd->GetAgents({2, 3}); // teams 2 and 3
TArray<int32> InBox = Crowd->GetAgentsInRect(Rect, {2});
TArray<int32> Marquee = Crowd->GetAgentsInScreenRect(PC, Min, Max, {1});
Because empty means "everything", adding the filter didn't change what any existing call returns.
Two queries exist specifically for this:
GetNearestAgent is the "find me a target" call - closest enemy to this unit, closest
friendly to this healer.
TIP
Filtering happens inside the simulation, over its own packed arrays. Pulling every agent into Blueprint and filtering there with a loop costs far more at high counts.
Team avoidance rules
By default every agent pushes every other agent using the global Strength and Settle Push values (see Avoidance). A team rule overrides both for one pair of teams:
Crowd->SetTeamAvoidance(1, 2, Strength, SettlePushStrength);
Rules are symmetric - (1, 2) and (2, 1) are the same rule, and setting one overwrites
the other. Passing the same team twice sets the rule within that team:
Crowd->SetTeamAvoidance(1, 1, 60.0f, -1.0f); // team 1 packs tightly among itself
Each of the two values is read the same way:
Setting both to 0 makes the two teams ignore each other completely and walk straight
through one another - useful for ghosts, spectators, or units on separate logical layers that
happen to share the map.
Crowd->SetTeamAvoidance(1, 2, 0.0f, 0.0f); // teams 1 and 2 pass through each other
Crowd->SetTeamAvoidance(1, 2, 0.0f, -1.0f); // don't jostle in transit, still step aside when idle
Crowd->SetTeamAvoidance(1, 2, -1.0f, 0.0f); // normal jostling; idle units hold their ground
Managing rules:
Cost
With no rules set, the avoidance loop does no team work at all - it checks once that the rule table is empty and skips every lookup. Measured against the same build without teams, the difference is inside run-to-run noise.
With rules active the lookup happens only for pairs that are actually touching, not every pair the spatial grid visits, which costs a few percent of tick time in dense crowds.
Seeing teams
Tick Show Teams in the Debug section to draw a coloured dot above every agent, one colour per team id. The agents nearest the camera also get their team id as text, out to Team Label Distance; the text is capped to keep the overlay readable in big crowds, while the dots are drawn for everyone.