replace DtPathCorridor comment

This commit is contained in:
ikpil 2024-02-14 01:34:16 +09:00
parent 389632def9
commit a5b3344c1f
1 changed files with 184 additions and 195 deletions

View File

@ -26,66 +26,80 @@ using DotRecast.Core.Numerics;
namespace DotRecast.Detour.Crowd namespace DotRecast.Detour.Crowd
{ {
/** /// Represents a dynamic polygon corridor used to plan agent movement.
* Represents a dynamic polygon corridor used to plan agent movement. /// @ingroup crowd, detour
*
* The corridor is loaded with a path, usually obtained from a #NavMeshQuery::FindPath() query. The corridor is then
* used to plan local movement, with the corridor automatically updating as needed to deal with inaccurate agent
* locomotion.
*
* Example of a common use case:
*
* -# Construct the corridor object and call -# Obtain a path from a #dtNavMeshQuery object. -# Use #Reset() to set the
* agent's current position. (At the beginning of the path.) -# Use #SetCorridor() to load the path and target. -# Use
* #FindCorners() to plan movement. (This handles dynamic path straightening.) -# Use #MovePosition() to feed agent
* movement back into the corridor. (The corridor will automatically adjust as needed.) -# If the target is moving, use
* #MoveTargetPosition() to update the end of the corridor. (The corridor will automatically adjust as needed.) -#
* Repeat the previous 3 steps to continue to move the agent.
*
* The corridor position and target are always constrained to the navigation mesh.
*
* One of the difficulties in maintaining a path is that floating point errors, locomotion inaccuracies, and/or local
* steering can result in the agent crossing the boundary of the path corridor, temporarily invalidating the path. This
* class uses local mesh queries to detect and update the corridor as needed to handle these types of issues.
*
* The fact that local mesh queries are used to move the position and target locations results in two beahviors that
* need to be considered:
*
* Every time a move function is used there is a chance that the path will become non-optimial. Basically, the further
* the target is moved from its original location, and the further the position is moved outside the original corridor,
* the more likely the path will become non-optimal. This issue can be addressed by periodically running the
* #OptimizePathTopology() and #OptimizePathVisibility() methods.
*
* All local mesh queries have distance limitations. (Review the #dtNavMeshQuery methods for details.) So the most
* accurate use case is to move the position and target in small increments. If a large increment is used, then the
* corridor may not be able to accurately find the new location. Because of this limiation, if a position is moved in a
* large increment, then compare the desired and resulting polygon references. If the two do not match, then path
* replanning may be needed. E.g. If you move the target, check #GetLastPoly() to see if it is the expected polygon.
*
*/
public class DtPathCorridor public class DtPathCorridor
{ {
private RcVec3f m_pos = new RcVec3f(); private RcVec3f m_pos = new RcVec3f();
private RcVec3f m_target = new RcVec3f(); private RcVec3f m_target = new RcVec3f();
private List<long> m_path; private List<long> m_path;
private static readonly float MIN_TARGET_DIST = RcMath.Sqr(0.01f);
/** /**
* Allocates the corridor's path buffer. @class dtPathCorridor
*/ @par
The corridor is loaded with a path, usually obtained from a #dtNavMeshQuery::findPath() query. The corridor
is then used to plan local movement, with the corridor automatically updating as needed to deal with inaccurate
agent locomotion.
Example of a common use case:
-# Construct the corridor object and call #init() to allocate its path buffer.
-# Obtain a path from a #dtNavMeshQuery object.
-# Use #reset() to set the agent's current position. (At the beginning of the path.)
-# Use #setCorridor() to load the path and target.
-# Use #findCorners() to plan movement. (This handles dynamic path straightening.)
-# Use #movePosition() to feed agent movement back into the corridor. (The corridor will automatically adjust as needed.)
-# If the target is moving, use #moveTargetPosition() to update the end of the corridor.
(The corridor will automatically adjust as needed.)
-# Repeat the previous 3 steps to continue to move the agent.
The corridor position and target are always constrained to the navigation mesh.
One of the difficulties in maintaining a path is that floating point errors, locomotion inaccuracies, and/or local
steering can result in the agent crossing the boundary of the path corridor, temporarily invalidating the path.
This class uses local mesh queries to detect and update the corridor as needed to handle these types of issues.
The fact that local mesh queries are used to move the position and target locations results in two beahviors that
need to be considered:
Every time a move function is used there is a chance that the path will become non-optimial. Basically, the further
the target is moved from its original location, and the further the position is moved outside the original corridor,
the more likely the path will become non-optimal. This issue can be addressed by periodically running the
#optimizePathTopology() and #optimizePathVisibility() methods.
All local mesh queries have distance limitations. (Review the #dtNavMeshQuery methods for details.) So the most accurate
use case is to move the position and target in small increments. If a large increment is used, then the corridor
may not be able to accurately find the new location. Because of this limiation, if a position is moved in a large
increment, then compare the desired and resulting polygon references. If the two do not match, then path replanning
may be needed. E.g. If you move the target, check #getLastPoly() to see if it is the expected polygon.
*/
public DtPathCorridor() public DtPathCorridor()
{ {
m_path = new List<long>(); m_path = new List<long>();
} }
/** /// @par
* Resets the path corridor to the specified position. ///
* /// @warning Cannot be called more than once.
* @param ref /// Allocates the corridor's path buffer.
* The polygon reference containing the position. /// @param[in] maxPath The maximum path size the corridor can handle.
* @param pos /// @return True if the initialization succeeded.
* The new position in the corridor. [(x, y, z)] public void Init(int maxPath)
*/ {
// ...
}
/// @par
///
/// Essentially, the corridor is set of one polygon in size with the target
/// equal to the position.
///
/// Resets the path corridor to the specified position.
/// @param[in] ref The polygon reference containing the position.
/// @param[in] pos The new position in the corridor. [(x, y, z)]
public void Reset(long refs, RcVec3f pos) public void Reset(long refs, RcVec3f pos)
{ {
m_path.Clear(); m_path.Clear();
@ -94,25 +108,27 @@ namespace DotRecast.Detour.Crowd
m_target = pos; m_target = pos;
} }
private static readonly float MIN_TARGET_DIST = RcMath.Sqr(0.01f);
/** /**
* Finds the corners in the corridor from the position toward the target. (The straightened path.) @par
*
* This is the function used to plan local movement within the corridor. One or more corners can be detected in This is the function used to plan local movement within the corridor. One or more corners can be
* order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath. detected in order to plan movement. It performs essentially the same function as #dtNavMeshQuery::findStraightPath.
*
* Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1) For example: If Due to internal optimizations, the maximum number of corners returned will be (@p maxCorners - 1)
* the buffers are sized to hold 10 corners, the function will never return more than 9 corners. So if 10 corners For example: If the buffers are sized to hold 10 corners, the function will never return more than 9 corners.
* are needed, the buffers should be sized for 11 corners. So if 10 corners are needed, the buffers should be sized for 11 corners.
*
* If the target is within range, it will be the last corner and have a polygon reference id of zero. If the target is within range, it will be the last corner and have a polygon reference id of zero.
* */
* @param filter /// Finds the corners in the corridor from the position toward the target. (The straightened path.)
* /// @param[out] cornerVerts The corner vertices. [(x, y, z) * cornerCount] [Size: <= maxCorners]
* @param[in] navquery The query object used to build the corridor. /// @param[out] cornerFlags The flag for each corner. [(flag) * cornerCount] [Size: <= maxCorners]
* @return Corners /// @param[out] cornerPolys The polygon reference for each corner. [(polyRef) * cornerCount]
*/ /// [Size: <= @p maxCorners]
/// @param[in] maxCorners The maximum number of corners the buffers can hold.
/// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
/// @return The number of corners returned in the corner buffers. [0 <= value <= @p maxCorners]
public int FindCorners(ref List<DtStraightPath> corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter) public int FindCorners(ref List<DtStraightPath> corners, int maxCorners, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
var result = navquery.FindStraightPath(m_pos, m_target, m_path, ref corners, maxCorners, 0); var result = navquery.FindStraightPath(m_pos, m_target, m_path, ref corners, maxCorners, 0);
@ -150,32 +166,28 @@ namespace DotRecast.Detour.Crowd
} }
/** /**
* Attempts to optimize the path if the specified point is visible from the current position. @par
*
* Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the 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. Non-optimal paths can 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. also form near the corners of tiles.
*
* This function uses an efficient local visibility search to try to optimize the corridor between the current This function uses an efficient local visibility search to try to optimize the corridor
* position and @p next. 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 The corridor will change only if @p next is visible from the current position and moving directly toward the point
* point is better than following the existing path. is better than following the existing path.
*
* The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency
* the call to match the needs to the agent. of the call to match the needs to the agent.
*
* This function is not suitable for long distance searches. This function is not suitable for long distance searches.
* */
* @param next /// Attempts to optimize the path if the specified point is visible from the current position.
* The point to search toward. [(x, y, z]) /// @param[in] next The point to search toward. [(x, y, z])
* @param pathOptimizationRange /// @param[in] pathOptimizationRange The maximum range to search. [Limit: > 0]
* The maximum range to search. [Limit: > 0] /// @param[in] navquery The query object used to build the corridor.
* @param navquery /// @param[in] filter The filter to apply to the operation.
* The query object used to build the corridor.
* @param filter
* The filter to apply to the operation.
*/
public void OptimizePathVisibility(RcVec3f next, float pathOptimizationRange, DtNavMeshQuery navquery, IDtQueryFilter filter) public void OptimizePathVisibility(RcVec3f next, float pathOptimizationRange, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Clamp the ray to max distance. // Clamp the ray to max distance.
@ -206,21 +218,18 @@ namespace DotRecast.Detour.Crowd
} }
/** /**
* Attempts to optimize the path using a local area search. (Partial replanning.) @par
*
* Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the Inaccurate locomotion or dynamic obstacle avoidance can force the agent position significantly outside the
* original corridor. Over time this can result in the formation of a non-optimal corridor. This function will use a original corridor. Over time this can result in the formation of a non-optimal corridor. This function will use a
* local area path search to try to re-optimize the corridor. local area path search to try to re-optimize the corridor.
*
* The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of The more inaccurate the agent movement, the more beneficial this function becomes. Simply adjust the frequency of
* the call to match the needs to the agent. the call to match the needs to the agent.
* */
* @param navquery /// Attempts to optimize the path using a local area search. (Partial replanning.)
* The query object used to build the corridor. /// @param[in] navquery The query object used to build the corridor.
* @param filter /// @param[in] filter The filter to apply to the operation.
* The filter to apply to the operation.
*
*/
public bool OptimizePathTopology(DtNavMeshQuery navquery, IDtQueryFilter filter, int maxIterations) public bool OptimizePathTopology(DtNavMeshQuery navquery, IDtQueryFilter filter, int maxIterations)
{ {
if (m_path.Count < 3) if (m_path.Count < 3)
@ -277,28 +286,26 @@ namespace DotRecast.Detour.Crowd
} }
/** /**
* Moves the position from the current location to the desired location, adjusting the corridor as needed to reflect @par
* the change.
* Behavior:
* Behavior:
* - The movement is constrained to the surface of the navigation mesh.
* - The movement is constrained to the surface of the navigation mesh. - The corridor is automatically adjusted - The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
* (shorted or lengthened) in order to remain valid. - The new position will be located in the adjusted corridor's - The new position will be located in the adjusted corridor's first polygon.
* first polygon.
* The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near'
* The expected use case is that the desired position will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc.
* depends on local polygon density, query search extents, etc.
* The resulting position will differ from the desired position if the desired position is not on the navigation mesh,
* The resulting position will differ from the desired position if the desired position is not on the navigation or it can't be reached using a local search.
* mesh, or it can't be reached using a local search. */
* /// Moves the position from the current location to the desired location, adjusting the corridor
* @param npos /// as needed to reflect the change.
* The desired new position. [(x, y, z)] /// @param[in] npos The desired new position. [(x, y, z)]
* @param navquery /// @param[in] navquery The query object used to build the corridor.
* The query object used to build the corridor. /// @param[in] filter The filter to apply to the operation.
* @param filter /// @return Returns true if move succeeded.
* The filter to apply to the operation.
*/
public bool MovePosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter) public bool MovePosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Move along navmesh and update new position. // Move along navmesh and update new position.
@ -323,22 +330,24 @@ namespace DotRecast.Detour.Crowd
} }
/** /**
* Moves the target from the curent location to the desired location, adjusting the corridor as needed to reflect @par
* the change. Behavior: - The movement is constrained to the surface of the navigation mesh. - The corridor is
* automatically adjusted (shorted or lengthened) in order to remain valid. - The new target will be located in the Behavior:
* adjusted corridor's last polygon.
* - The movement is constrained to the surface of the navigation mesh.
* The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' - The corridor is automatically adjusted (shorted or lengthened) in order to remain valid.
* depends on local polygon density, query search extents, etc. The resulting target will differ from the desired - The new target will be located in the adjusted corridor's last polygon.
* target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
* The expected use case is that the desired target will be 'near' the current corridor. What is considered 'near' depends on local polygon density, query search half extents, etc.
* @param npos
* The desired new target position. [(x, y, z)] The resulting target will differ from the desired target if the desired target is not on the navigation mesh, or it can't be reached using a local search.
* @param navquery */
* The query object used to build the corridor. /// Moves the target from the curent location to the desired location, adjusting the corridor
* @param filter /// as needed to reflect the change.
* The filter to apply to the operation. /// @param[in] npos The desired new target position. [(x, y, z)]
*/ /// @param[in] navquery The query object used to build the corridor.
/// @param[in] filter The filter to apply to the operation.
/// @return Returns true if move succeeded.
public bool MoveTargetPosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter) public bool MoveTargetPosition(RcVec3f npos, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Move along navmesh and update new position. // Move along navmesh and update new position.
@ -360,16 +369,16 @@ namespace DotRecast.Detour.Crowd
return false; return false;
} }
/** /// @par
* Loads a new path and target into the corridor. The current corridor position is expected to be within the first ///
* polygon in the path. The target is expected to be in the last polygon. /// The current corridor position is expected to be within the first polygon in the path. The target
* /// is expected to be in the last polygon.
* @warning The size of the path must not exceed the size of corridor's path buffer set during #Init(). ///
* @param target /// @warning The size of the path must not exceed the size of corridor's path buffer set during #init().
* The target location within the last polygon of the path. [(x, y, z)] /// Loads a new path and target into the corridor.
* @param path /// @param[in] target The target location within the last polygon of the path. [(x, y, z)]
* The path corridor. /// @param[in] path The path corridor. [(polyRef) * @p npolys]
*/ /// @param[in] npath The number of polygons in the path.
public void SetCorridor(RcVec3f target, List<long> path) public void SetCorridor(RcVec3f target, List<long> path)
{ {
m_target = target; m_target = target;
@ -427,19 +436,15 @@ namespace DotRecast.Detour.Crowd
return true; return true;
} }
/** /// @par
* Checks the current corridor path to see if its polygon references remain valid. The path can be invalidated if ///
* there are structural changes to the underlying navigation mesh, or the state of a polygon within the path changes /// The path can be invalidated if there are structural changes to the underlying navigation mesh, or the state of
* resulting in it being filtered out. (E.g. An exclusion or inclusion flag changes.) /// a polygon within the path changes resulting in it being filtered out. (E.g. An exclusion or inclusion flag changes.)
* /// Checks the current corridor path to see if its polygon references remain valid.
* @param maxLookAhead ///
* The number of polygons from the beginning of the corridor to search. /// @param[in] maxLookAhead The number of polygons from the beginning of the corridor to search.
* @param navquery /// @param[in] navquery The query object used to build the corridor.
* The query object used to build the corridor. /// @param[in] filter The filter to apply to the operation.
* @param filter
* The filter to apply to the operation.
* @return
*/
public bool IsValid(int maxLookAhead, DtNavMeshQuery navquery, IDtQueryFilter filter) public bool IsValid(int maxLookAhead, DtNavMeshQuery navquery, IDtQueryFilter filter)
{ {
// Check that all polygons still pass query filter. // Check that all polygons still pass query filter.
@ -455,59 +460,43 @@ namespace DotRecast.Detour.Crowd
return true; return true;
} }
/** /// Gets the current position within the corridor. (In the first polygon.)
* Gets the current position within the corridor. (In the first polygon.) /// @return The current position within the corridor.
*
* @return The current position within the corridor.
*/
public RcVec3f GetPos() public RcVec3f GetPos()
{ {
return m_pos; return m_pos;
} }
/** /// Gets the current target within the corridor. (In the last polygon.)
* Gets the current target within the corridor. (In the last polygon.) /// @return The current target within the corridor.
*
* @return The current target within the corridor.
*/
public RcVec3f GetTarget() public RcVec3f GetTarget()
{ {
return m_target; return m_target;
} }
/** /// The polygon reference id of the first polygon in the corridor, the polygon containing the position.
* The polygon reference id of the first polygon in the corridor, the polygon containing the position. /// @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
*
* @return The polygon reference id of the first polygon in the corridor. (Or zero if there is no path.)
*/
public long GetFirstPoly() public long GetFirstPoly()
{ {
return 0 == m_path.Count ? 0 : m_path[0]; return 0 == m_path.Count ? 0 : m_path[0];
} }
/** /// The polygon reference id of the last polygon in the corridor, the polygon containing the target.
* The polygon reference id of the last polygon in the corridor, the polygon containing the target. /// @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
*
* @return The polygon reference id of the last polygon in the corridor. (Or zero if there is no path.)
*/
public long GetLastPoly() public long GetLastPoly()
{ {
return 0 == m_path.Count ? 0 : m_path[m_path.Count - 1]; return 0 == m_path.Count ? 0 : m_path[m_path.Count - 1];
} }
/** /// The corridor's path.
* The corridor's path. /// @return The corridor's path. [(polyRef) * #getPathCount()]
*/
public List<long> GetPath() public List<long> GetPath()
{ {
return m_path; return m_path;
} }
/** /// The number of polygons in the current corridor path.
* The number of polygons in the current corridor path. /// @return The number of polygons in the current corridor path.
*
* @return The number of polygons in the current corridor path.
*/
public int GetPathCount() public int GetPathCount()
{ {
return m_path.Count; return m_path.Count;