Crowds and avoidance

Avoidance steers agents round each other and round obstacles by velocity obstacles (ORCA). It is a component of its own that needs no navmesh: it works on the ground plane, or in space for fliers.

mnavAvoidanceDef def = mnavDefaultAvoidanceDef();
mnavAvoidance* avoidance = NULL;
mnavResult made = mnavCreateAvoidance(&def, &avoidance);

// Each step: positions, velocities and preferred velocities in, new
// velocities out.
mnavResult stepped =
    mnavAvoidance_Avoid(avoidance, agents, agentCount, obstacles, obstacleCount, 0.1, velocities);

Avoidance is local. An agent whose preferred velocity points into a wall waits at its face: going round is a path's work.

A crowd on a navmesh

The library gives the parts of a crowd and leaves the agents to the host. Each step, each agent's corridor gives its corners, mnavSteer the velocity it wants, mnavAvoidance_Avoid one clear of the others, and the host moves it along the surface. samples/navcrowd.c is a complete program.

mnavSteerDef steer = mnavDefaultSteerDef();   // set maxSpeed to the agents'
for (int32_t i = 0; i < count; ++i)
{
    mnavCorners corners;
    mnavSteering steering;
    if (mnavQuery_FindCorridorCorners(query, &corridors[i], navmesh, &corners) == mnav_success &&
        mnavSteer(&corners, &steer, &steering) == mnav_success)
    {
        agents[i].preferred = (mnavVec2){steering.velocity.x, steering.velocity.z};
    }
}
mnavResult avoided = mnavAvoidance_Avoid(avoidance, agents, count, obstacles, obstacleCount, dt, velocities);
for (int32_t i = 0; i < count && avoided == mnav_success; ++i)
{
    // Limit the change from agents[i].velocity by the agent's acceleration.
    mnavPos3 at = corridors[i].position;
    mnavPos3 wanted = {at.x + (double)velocities[i].x * dt, at.y,
                       at.z + (double)velocities[i].y * dt};
    if (mnavQuery_MoveCorridor(query, &corridors[i], navmesh, wanted, NULL, NULL) == mnav_success)
    {
        agents[i].position = (mnavPos2){corridors[i].position.x, corridors[i].position.z};
        agents[i].velocity = velocities[i];
    }
}

Fliers

Fliers sharing a flight volume avoid each other in space with mnavAvoidance_Avoid3D, on the same avoidance set. mnavAgent3D has the fields of mnavAgent in three dimensions, and its obstacles are spheres (mnavSphere), still or moving, which count against the set's obstacle points.

Walls and terrain are the flight volume's to keep clear of: a flier follows its path and avoids what moves, and the host keeps each step in open space with mnavFlightVolume_Raycast, sliding along the axes when a step is blocked (samples/flight.c). A held-back flier sidesteps to its right, +Y being up, and one rising or falling straight sidesteps toward +X or -X, so that two meeting head on pass instead of stalling.