Crowd: Detail API documentation for primary members declared in DetourCrowd.h.

Also includes some minor updates to the path corridor documentation.
This commit is contained in:
Stephen Pratt 2011-10-04 18:57:17 +00:00
parent 588d674c72
commit 0ec4f993cb
4 changed files with 315 additions and 22 deletions

View File

@ -26,66 +26,121 @@
#include "DetourProximityGrid.h"
#include "DetourPathQueue.h"
/// The maximum number of neighbors that a crowd agent can take into account
/// for steering decisions.
/// @ingroup crowd
static const int DT_CROWDAGENT_MAX_NEIGHBOURS = 6;
/// The maximum number of corners a crowd agent will look ahead in the path.
/// This value is used for sizing the crowd agent corner buffers.
/// Due to the behavior of the crowd manager, the actual number of useful
/// corners will be one less than this number.
/// @ingroup crowd
static const int DT_CROWDAGENT_MAX_CORNERS = 4;
/// The maximum number of crowd avoidance configurations supported by the
/// crowd manager.
/// @ingroup crowd
/// @see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(), dtCrowd::getObstacleAvoidanceParams(),
/// dtCrowdAgentParams::obstacleAvoidanceType
static const int DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS = 8;
/// Provides neighbor data for agents managed by the crowd.
/// @ingroup crowd
/// @see dtCrowdAgent::neis, dtCrowd
struct dtCrowdNeighbour
{
int idx;
float dist;
int idx; ///< The index of the neighbor in the crowd.
float dist; ///< The distance between the current agent and the neighbor.
};
/// The type of navigation mesh polygon the agent is currently traversing.
/// @ingroup crowd
enum CrowdAgentState
{
DT_CROWDAGENT_STATE_INVALID,
DT_CROWDAGENT_STATE_WALKING,
DT_CROWDAGENT_STATE_OFFMESH,
DT_CROWDAGENT_STATE_INVALID, ///< The agent is not in a valid state.
DT_CROWDAGENT_STATE_WALKING, ///< The agent is traversing a normal navigation mesh polygon.
DT_CROWDAGENT_STATE_OFFMESH, ///< The agent is traversing an off-mesh connection.
};
/// Configuration parameters for a crowd agent.
/// @ingroup crowd
struct dtCrowdAgentParams
{
float radius;
float height;
float maxAcceleration;
float maxSpeed;
float radius; ///< Agent radius. [Limit: >= 0]
float height; ///< Agent height. [Limit: > 0]
float maxAcceleration; ///< Maximum allowed acceleration. [Limit: >= 0]
float maxSpeed; ///< Maximum allowed speed. [Limit: >= 0]
/// Defines how close a collision element must be before it is considered for steering behaviors. [Limits: > 0]
float collisionQueryRange;
float pathOptimizationRange;
float pathOptimizationRange; ///< The path visibility optimization range. [Limit: > 0]
/// How aggresive the agent manager should be at avoiding collisions with this agent. [Limit: >= 0]
float separationWeight;
/// Flags that impact steering behavior. (See: #UpdateFlags)
unsigned char updateFlags;
unsigned char obstacleAvoidanceType;
/// The index of the avoidance configuration to use for the agent.
/// [Limits: 0 <= value <= #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
unsigned char obstacleAvoidanceType;
/// User defined data attached to the agent.
void* userData;
};
/// Represents an agent managed by a #dtCrowd object.
/// @ingroup crowd
struct dtCrowdAgent
{
/// 1 if the agent is active, or 0 if the agent is in an unused slot in the agent pool.
unsigned char active;
/// The type of mesh polygon the agent is traversing. (See: #CrowdAgentState)
unsigned char state;
/// The path corridor the agent is using.
dtPathCorridor corridor;
/// The local boundary data for the agent.
dtLocalBoundary boundary;
float t;
float var;
/// The last time the agent's path corridor was optimized.
float topologyOptTime;
/// The known neighbors of the agent.
dtCrowdNeighbour neis[DT_CROWDAGENT_MAX_NEIGHBOURS];
/// The number of neighbors.
int nneis;
/// The desired speed.
float desiredSpeed;
float npos[3];
float npos[3]; ///< The current agent position. [(x, y, z)]
float disp[3];
float dvel[3];
float dvel[3]; ///< The desired velocity of the agent. [(x, y, z)]
float nvel[3];
float vel[3];
float vel[3]; ///< The actual velocity of the agent. [(x, y, z)]
/// The agent's configuration parameters.
dtCrowdAgentParams params;
/// The local path corridor corners for the agent. (Staight path.) [(x, y, z) * #ncorners]
float cornerVerts[DT_CROWDAGENT_MAX_CORNERS*3];
/// The local path corridor corner flags. (See: #dtStraightPathFlags) [(flags) * #ncorners]
unsigned char cornerFlags[DT_CROWDAGENT_MAX_CORNERS];
/// The reference id of the polygon being entered at the corner. [(polyRef) * #ncorners]
dtPolyRef cornerPolys[DT_CROWDAGENT_MAX_CORNERS];
/// The number of corners.
int ncorners;
};
@ -97,13 +152,16 @@ struct dtCrowdAgentAnimation
float t, tmax;
};
/// Crowd agent update flags.
/// @ingroup crowd
/// @see dtCrowdAgentParams::updateFlags
enum UpdateFlags
{
DT_CROWD_ANTICIPATE_TURNS = 1,
DT_CROWD_OBSTACLE_AVOIDANCE = 2,
DT_CROWD_SEPARATION = 4,
DT_CROWD_OPTIMIZE_VIS = 8,
DT_CROWD_OPTIMIZE_TOPO = 16,
DT_CROWD_OPTIMIZE_VIS = 8, ///< Use #dtPathCorridor::optimizePathVisibility() to optimize the agent path.
DT_CROWD_OPTIMIZE_TOPO = 16, ///< Use dtPathCorridor::optimizePathTopology() to optimize the agent path.
};
struct dtCrowdAgentDebugInfo
@ -113,6 +171,8 @@ struct dtCrowdAgentDebugInfo
dtObstacleAvoidanceDebugData* vod;
};
/// Provides local steering behaviors for a group of agents.
/// @ingroup crowd
class dtCrowd
{
int m_maxAgents;
@ -182,37 +242,201 @@ public:
dtCrowd();
~dtCrowd();
/// Initializes the crowd.
/// @param[in] maxAgents The maximum number of agents the crowd can manage. [Limit: >= 1]
/// @param[in] maxAgentRadius The maximum radius of any agent that will be added to the crowd. [Limit: > 0]
/// @param[in] nav The navigation mesh to use for planning.
/// @return True if the initialization succeeded.
bool init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav);
/// Sets the shared avoidance configuration for the specified index.
/// @param[in] idx The index. [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
/// @param[in] params The new configuration.
void setObstacleAvoidanceParams(const int idx, const dtObstacleAvoidanceParams* params);
/// Gets the shared avoidance configuration for the specified index.
/// @param[in] idx The index of the configuration to retreive.
/// [Limits: 0 <= value < #DT_CROWD_MAX_OBSTAVOIDANCE_PARAMS]
/// @return The requested configuration.
const dtObstacleAvoidanceParams* getObstacleAvoidanceParams(const int idx) const;
/// Gets the specified agent from the pool.
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
/// @return The requested agent.
const dtCrowdAgent* getAgent(const int idx);
/// The maximum number of agents that can be managed by the object.
/// @return The maximum number of agents.
const int getAgentCount() const;
/// Adds a new agent to the crowd.
/// @param[in] pos The requested position of the agent. [(x, y, z)]
/// @param[in] params The configutation of the agent.
/// @return The index of the agent in the agent pool. Or -1 if the agent could not be added.
int addAgent(const float* pos, const dtCrowdAgentParams* params);
/// Updates the specified agent's configuration.
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
/// @param[in] params The new agent configuration.
void updateAgentParameters(const int idx, const dtCrowdAgentParams* params);
/// Removes the agent from the crowd.
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
void removeAgent(const int idx);
/// Submits a new move request for the specified agent.
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
/// @param[in] ref The position's polygon reference.
/// @param[in] pos The position within the polygon. [(x, y, z)]
/// @return True if the request was successfully submitted.
bool requestMoveTarget(const int idx, dtPolyRef ref, const float* pos);
/// Sumbits a request to adjust the target position of the specified agent.
/// @param[in] idx The agent index. [Limits: 0 <= value < #getAgentCount()]
/// @param[in] ref The position's polygon reference.
/// @param[in] pos The position within the polygon. [(x, y, z)]
/// @return True if the request was successfully submitted.
bool adjustMoveTarget(const int idx, dtPolyRef ref, const float* pos);
/// Gets the active agents int the agent pool.
/// @param[out] agents An array of agent pointers. [(#dtCrowdAgent *) * maxAgents]
/// @param[in] maxAgents The size of the crowd agent array.
/// @return The number of agents returned in @p agents.
int getActiveAgents(dtCrowdAgent** agents, const int maxAgents);
/// Updates the steering and positions of all agents.
/// @param[in] dt The time, in seconds, to update the simulation. [Limit: > 0]
/// @param[out] debug A debug object to load with debug information. [Opt]
void update(const float dt, dtCrowdAgentDebugInfo* debug);
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
const dtQueryFilter* getFilter() const { return &m_filter; }
/// Gets the filter used by the crowd.
/// @return The filter used by the crowd.
dtQueryFilter* getEditableFilter() { return &m_filter; }
/// Gets the search extents [(x, y, z)] used by the crowd for query operations.
/// @return The search extents used by the crowd. [(x, y, z)]
const float* getQueryExtents() const { return m_ext; }
/// Gets the velocity sample count.
/// @return The velocity sample count.
inline int getVelocitySampleCount() const { return m_velocitySampleCount; }
/// Gets the crowd's proximity grid.
/// @return The crowd's proximity grid.
const dtProximityGrid* getGrid() const { return m_grid; }
/// Gets the crowd's path request queue.
/// @return The crowd's path request queue.
const dtPathQueue* getPathQueue() const { return &m_pathq; }
/// Gets the query object used by the crowd.
const dtNavMeshQuery* getNavMeshQuery() const { return m_navquery; }
};
/// Allocates a crowd object using the Detour allocator.
/// @return A crowd object that is ready for initialization, or null on failure.
/// @ingroup crowd
dtCrowd* dtAllocCrowd();
/// Frees the specified crowd object using the Detour allocator.
/// @param[in] ptr A crowd object allocated using #dtAllocCrowd
/// @ingroup crowd
void dtFreeCrowd(dtCrowd* ptr);
#endif // DETOURCROWD_H
///////////////////////////////////////////////////////////////////////////
// This section contains detailed documentation for members that don't have
// a source file. It reduces clutter in the main section of the header.
/**
@defgroup crowd Crowd
Members in this module implement local steering and dynamic avoidance features.
The crowd is the big beast of the navigation features. It not only handles a
lot of the path management for you, but also local steering and dynamic
avoidance between members of the crowd. I.e. It can keep your agents from
running into each other.
Main class: #dtCrowd
The #dtNavMeshQuery and #dtPathCorridor classes provide perfectly good, easy
to use path planning features. But in the end they only give you points that
your navigation client should be moving toward. When it comes to deciding things
like agent velocity and steering to avoid other agents, that is up to you to
implement. Unless, of course, you decide to use #dtCrowd.
Basically, you add an agent to the crowd, providing various configuration
settings such as maximum speed and acceleration. You also provide a local
target to more toward. The crowd manager then provides, with every update, the
new agent position and velocity for the frame. The movement will be
constrained to the navigation mesh, and steering will be applied to ensure
agents managed by the crowd do not collide with each other.
This is very powerful feature set. But it comes with limitations.
The biggest limitation is that you must give control of the agent's position
completely over to the crowd manager. You can update things like maximum speed
and acceleration. But in order for the crowd manager to do its thing, it can't
allow you to constantly be giving it overrides to position and velocity. So
you give up direct control of the agent's movement. It belongs to the crowd.
The second biggest limitation revolves around the fact that the crowd manager
deals with local planning. So the agent's target should never be more than
256 polygons aways from its current position. If it is, you risk
your agent failing to reach its target. So you may still need to do long
distance planning and provide the crowd manager with intermediate targets.
Other significant limitations:
- All agents using the crowd manager will use the same #dtQueryFilter.
- Crowd management is relatively expensive. The maximum agents under crowd
management at any one time is between 20 and 30. A good place to start
is a maximum of 25 agents for 0.5ms per frame.
@note This is a summary list of members. Use the index or search
feature to find minor members.
@struct dtCrowdAgentParams
@see dtCrowdAgent, dtCrowd::addAgent(), dtCrowd::updateAgentParameters()
@var dtCrowdAgentParams::obstacleAvoidanceType
@par
#dtCrowd permits agents to use different avoidance configurations. This value
is the index of the #dtObstacleAvoidanceParams within the crowd.
@see dtObstacleAvoidanceParams, dtCrowd::setObstacleAvoidanceParams(),
dtCrowd::getObstacleAvoidanceParams()
@var dtCrowdAgentParams::collisionQueryRange
@par
Collision elements include other agents and navigation mesh boundaries.
This value is often based on the agent radius and/or maximum speed. E.g. radius * 8
@var dtCrowdAgentParams::pathOptimizationRange
@par
Only applicalbe if #updateFlags includes the #DT_CROWD_OPTIMIZE_VIS flag.
This value is often based on the agent radius. E.g. radius * 30
@see dtPathCorridor::optimizePathVisibility()
@var dtCrowdAgentParams::separationWeight
@par
A higher value will result in agents trying to stay farther away from each other at
the cost of more difficult steering in tight spaces.
*/

View File

@ -21,7 +21,8 @@
#include "DetourNavMeshQuery.h"
/// Represents a dynamic polygon corridor used to plan agent movement
/// Represents a dynamic polygon corridor used to plan agent movement.
/// @ingroup crowd, detour
class dtPathCorridor
{
float m_pos[3];

View File

@ -218,7 +218,43 @@ static int getNeighbours(const float* pos, const float height, const float range
return n;
}
/**
@class dtCrowd
@par
This is the core class of the @ref crowd module. See the @ref crowd documentation for a summary
of the crowd features.
A common method for setting up the crowd is as follows:
-# Allocate the crowd using #dtAllocCrowd.
-# Initialize the crowd using #init().
-# Set the avoidance configurations using #setObstacleAvoidanceParams().
-# Add agents using #addAgent() and make an initial movement request using #requestMoveTarget().
A common process for managing the crowd is as follows:
-# Call #update() to allow the crowd to manage its agents.
-# Retrieve agent information using #getActiveAgents().
-# Make movement requests using #requestMoveTarget() and #adjustMoveTarget().
-# Repeat every frame.
Some agent configuration settings can be updated using #updateAgentParameters(). But the crowd owns the
agent position. So it is not possible to update an active agent's position. If agent position
must be fed back into the crowd, the agent must be removed and re-added.
Notes:
- Path related information is available for newly added agents only after an #update() has been
performed.
- Agent objects are kept in a pool and re-used. So it is important when using agent objects to check the value of
#dtCrowdAgent::active to determine if the agent is actually in use or not.
- This class is meant to provide 'local' movement. There is a limit of 256 polygons in the path corridor.
So it is not meant to provide automatic pathfinding services over long distances.
@see dtAllocCrowd(), dtFreeCrowd(), init(), dtCrowdAgent
*/
dtCrowd::dtCrowd() :
m_maxAgents(0),
@ -273,6 +309,9 @@ void dtCrowd::purge()
m_navquery = 0;
}
/// @par
///
/// May be called more than once to purge and re-initialize the crowd.
bool dtCrowd::init(const int maxAgents, const float maxAgentRadius, dtNavMesh* nav)
{
purge();
@ -378,6 +417,9 @@ const int dtCrowd::getAgentCount() const
return m_maxAgents;
}
/// @par
///
/// Agents in the pool may not be in use. Check #dtCrowdAgent.active before using the returned object.
const dtCrowdAgent* dtCrowd::getAgent(const int idx)
{
return &m_agents[idx];
@ -390,6 +432,9 @@ void dtCrowd::updateAgentParameters(const int idx, const dtCrowdAgentParams* par
memcpy(&m_agents[idx].params, params, sizeof(dtCrowdAgentParams));
}
/// @par
///
/// The agent's position will be constrained to the surface of the navigation mesh.
int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
{
// Find empty slot.
@ -439,6 +484,10 @@ int dtCrowd::addAgent(const float* pos, const dtCrowdAgentParams* params)
return idx;
}
/// @par
///
/// The agent is deactivated and will no longer be processed. Its #dtCrowdAgent object
/// is not removed from the pool. It is marked as inactive so that it is available for reuse.
void dtCrowd::removeAgent(const int idx)
{
if (idx >= 0 && idx < m_maxAgents)
@ -505,7 +554,15 @@ bool dtCrowd::requestMoveTargetReplan(const int idx, dtPolyRef ref, const float*
return true;
}
/// @par
///
/// This method is used when a new target is set. Use #adjustMoveTarget() when
/// only small local adjustments are needed. (Such as happens when following a
/// moving target.)
///
/// The position will be constrained to the surface of the navigation mesh.
///
/// The request will be processed during the next #update().
bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
{
if (idx < 0 || idx > m_maxAgents)
@ -545,7 +602,15 @@ bool dtCrowd::requestMoveTarget(const int idx, dtPolyRef ref, const float* pos)
return true;
}
/// @par
///
/// This method is used when to make small local adjustments to the current
/// target. (Such as happens when following a moving target.) Use
/// #requestMoveTarget() when a new target is needed.
///
/// The position will be constrained to the surface of the navigation mesh.
///
/// The request will be processed during the next #update().
bool dtCrowd::adjustMoveTarget(const int idx, dtPolyRef ref, const float* pos)
{
if (idx < 0 || idx > m_maxAgents)

View File

@ -293,8 +293,11 @@ int dtPathCorridor::findCorners(float* cornerVerts, unsigned char* cornerFlags,
@par
Inaccurate locomotion or dynamic obstacle avoidance can force the argent position significantly outside the
original corridor. Over time this can result in the formation of a non-optimal corridor. This function uses an
efficient local visibility search to try to re-optimize the corridor between the current position and @p next.
original corridor. Over time this can result in the formation of a non-optimal corridor. Non-optimal paths can
also form near the corners of tiles.
This function uses an efficient local visibility search to try to optimize the corridor
between the current position and @p next.
The corridor will change only if @p next is visible from the current position and moving directly toward the point
is better than following the existing path.