/// <summary> /// Moves over an off-mesh connection. /// </summary> /// <remarks> /// <para> /// This method is minimally tested and documented. /// </para> /// </remarks> /// <param name="connectionRef">The connection polygon reference.</param> /// <param name="endpointRefs">Polygon endpoint references. [Length: 2]</param> /// <param name="startPosition">The start position.</param> /// <param name="endPosition">The end position.</param> /// <returns>True if the operation succeeded.</returns> public bool MoveOverConnection(uint connectionRef, uint[] endpointRefs , Vector3 startPosition, Vector3 endPosition) { return(PathCorridorEx.dtpcMoveOverOffmeshConnection(mRoot , connectionRef, endpointRefs, ref startPosition, ref endPosition, ref mPosition , mQuery.root)); }
/// <summary> /// Moves the position and target from their curent locations to the desired locations. /// </summary> /// <remarks> /// <para> /// Performs an aggregrate operation in the following order: /// </para> /// <ol> /// <li><see cref="MoveTarget"/></li> /// <li><see cref="MovePosition"/></li> /// </ol> /// <para> /// See the documentation of the related functions for details on behavior. /// </para> /// <para> /// This method is more efficient than calling the other methods individually. /// </para> /// </remarks> /// <param name="desiredPosition">The desired position.</param> /// <param name="desiredTarget">The desired target.</param> public void Move(Vector3 desiredPosition, Vector3 desiredTarget) { mCorners.cornerCount = PathCorridorEx.dtpcMove(mRoot , ref desiredPosition, ref desiredTarget, ref mPosition, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); }
/// <summary> /// Finds the corners in the corridor from the position toward the target. /// (The straightened path.) /// </summary> /// <remarks> /// <para> /// This method can be used to do corner searches that exceed the capacity of the /// corridor's normal corner buffers. /// </para> /// <para> /// This method performs essentially the same function as /// <see cref="NavmeshQuery.GetStraightPath"/>. /// </para> /// <para> /// Due to internal optimizations, the actual maximum number of corners returned /// will be <c>(buffer.MaxCorners - 1)</c> /// </para> /// <para> /// If the target is within range, it will be the last corner and have a polygon /// reference of zero. /// </para> /// <para> /// Behavior is undefined if the buffer structure is malformed. E.g. The flag and polygon /// buffers are different sizes. /// </para> /// </remarks> /// <param name="buffer">The buffer to load the results into. [Length: >= 2]</param> /// <returns>The number of corners returned in the buffers.</returns> public int FindCorners(CornerData buffer) { buffer.cornerCount = PathCorridorEx.dtpcFindCorners(mRoot , buffer.verts, buffer.flags, buffer.polyRefs, buffer.polyRefs.Length , mQuery.root, mFilter.root); return(buffer.cornerCount); }
/// <summary> /// Immediately frees all unmanaged resources allocated by the object. /// </summary> public void RequestDisposal() { if (!IsDisposed) { PathCorridorEx.dtpcFree(mRoot); mRoot = IntPtr.Zero; } }
/// <summary> /// Loads a new path and target into the corridor. /// </summary> /// <remarks> /// <para> /// The current position is expected to be within the first /// polygon in the path. The target is expected to be in the last /// polygon. /// </para> /// </remarks> /// <param name="target">The target location within the last polygon of the path.</param> /// <param name="path"> /// The path corridor. [(polyRef) * <paramref name="pathCount"/>] /// </param> /// <param name="pathCount"> /// The number of polygons in the path. /// [Limits: 0 <= value <= <see cref="MaxPathSize"/>] /// </param> public void SetCorridor(Vector3 target , uint[] path , int pathCount) { mCorners.cornerCount = PathCorridorEx.dtpcSetCorridor(mRoot , ref target, path, pathCount, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); }
/// <summary> /// Moves the target from its curent location to the desired location, adjusting the /// corridor as needed to reflect the change. /// </summary> /// <remarks> /// <para> /// Behavior: /// </para> /// <ul> /// <li>The movement is constrained to the surface of the navigation mesh.</li> /// <li>The corridor is automatically adjusted (shorted or lengthened) and /// <see cref="Corners"/> updated in order to remain valid.</li> /// <li>The new position will be located in the adjusted corridor's last polygon.</li> /// </ul> /// <para> /// The expected use case: The desired target will be 'near' the corridor. What is /// considered 'near' depends on local polygon density, query search extents, etc. /// </para> /// <para> /// 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. /// </para> /// </remarks> /// <param name="desiredTarget">The desired target.</param> /// <returns>The result of the move.</returns> public NavmeshPoint MoveTarget(Vector3 desiredTarget) { mCorners.cornerCount = PathCorridorEx.dtpcMoveTargetPosition(mRoot , ref desiredTarget, ref mTarget , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); return(mTarget); }
/// <summary> /// Resets the corridor to the specified position. /// </summary> /// <remarks> /// <para> /// This method sets the position and target to the specified location, and reduces the /// corridor to the location's polygon. (Path size = 1) /// </para> /// <para> /// This method does not perform any validation of the input data. /// </para> /// </remarks> /// <param name="position">The position of the client.</param> public void Reset(NavmeshPoint position) { PathCorridorEx.dtpcReset(mRoot, position); mPosition = position; mTarget = position; mCorners.cornerCount = 1; mCorners.verts[0] = position.point; mCorners.flags[0] = WaypointFlag.Start | WaypointFlag.End; mCorners.polyRefs[0] = position.polyRef; }
/// <summary> /// Attempts to optimize the path using a local area search. /// (Partial replanning.) /// </summary> /// <remarks> /// <para> /// Improves pathfinding appearance in crowded areas and for complex meshes. /// </para> /// <para> /// The only time <paramref name="updateCorners"/> should be set to false is if a move or /// another optimization method is to be called next. Otherwise the corner data may /// become invalid. /// </para> /// <para> /// Inaccurate locomotion or dynamic obstacle avoidance can force the client position /// significantly outside the original corridor. Over time this can result in the /// formation of a non-optimal corridor. This method will use a local area path search /// to try to re-optimize the corridor. /// </para> /// <para> /// The more inaccurate the client movement, the more beneficial this method becomes. /// Simply adjust the frequency of the call to match the needs to the client. /// </para> /// <para> /// This is a local optimization. It usually doesn't effect the entire corridor /// through to the goal. It should normally be called based on a time increment rather /// than movement events. I.e. Call once a second. /// </para> /// </remarks> /// <param name="updateCorners">True if the corners data should be refreshed.</param> public void OptimizePathTopology(bool updateCorners) { if (updateCorners) { mCorners.cornerCount = PathCorridorEx.dtpcOptimizePathTopologyExt(mRoot , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); } else { PathCorridorEx.dtpcOptimizePathTopology(mRoot, mQuery.root, mFilter.root); } }
/// <summary> /// Attempts to optimize the path if the specified point is visible from the current /// position. /// </summary> /// <remarks> /// <para> /// Improves pathfinding appearance when using meshes that contain non-border. /// vertices. (E.g. Tiled meshes and meshes constructed using multiple areas.) /// </para> /// <para> /// The only time <paramref name="updateCorners"/> should be set to false is if a move /// or other optimization method is to be called next. Otherwise the corner data may /// become invalid. /// </para> /// <para> /// 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. A non-optimal corridor can also form near /// non-border vertices. (I.e. At tile corners or area transitions.) /// </para> /// <para> /// This function uses an efficient local visibility search to try to optimize the corridor /// between the current position and <paramref name="next"/>. /// </para> /// <para> /// The corridor will change only if <paramref name="next"/> is visible from the /// current position and moving directly toward the point is better than following the /// existing path. /// </para> /// <para> /// The more inaccurate the client movement, the more beneficial this method becomes. /// Simply adjust the frequency of the call to match the needs to the client. /// </para> /// <para> /// This method is not suitable for long distance searches. /// </para> /// </remarks> /// <param name="next">The point to search toward.</param> /// <param name="optimizationRange">The maximum range to search. [Limit: > 0]</param> /// <param name="updateCorners">True if the corners data should be refreshed.</param> public void OptimizePathVisibility(Vector3 next, float optimizationRange , bool updateCorners) { if (updateCorners) { mCorners.cornerCount = PathCorridorEx.dtpcOptimizePathVisibilityExt(mRoot , ref next, optimizationRange , mCorners.verts, mCorners.flags, mCorners.polyRefs, mCorners.polyRefs.Length , mQuery.root, mFilter.root); } else { PathCorridorEx.dtpcOptimizePathVisibility( mRoot, ref next, optimizationRange, mQuery.root, mFilter.root); } }
/// <summary> /// Constructor. /// </summary> /// <remarks> /// <para> /// <b>Important:</b> The <see cref="Reset"/> method must be called before the corridor /// can be used. (That is how the position is set.) /// </para> /// <para> /// Due to internal optimizations, the maximum number of detectable corners will be /// <c>(<paramref name="maxCorners"/> - 1)</c>. /// </para> /// <para>The query and filter parameters can be set to null. This supports the ability /// to create pools of re-usable path corridor objects. But it means that care needs to /// be taken not to use the corridor until query and filter objects have been set. /// See <see cref="ReleaseLocals"/> and <see cref="LoadLocals"/> for pool related utility /// functions. /// </para> /// </remarks> /// <param name="maxPathSize"> /// The maximum path size that can be handled by the object. [Limit: >= 1] /// </param> /// <param name="maxCorners"> /// The maximum number of corners the corner buffer can hold. [Limit: >= 2] /// </param> /// <param name="query">The query to be used by the corridor.</param> /// <param name="filter">The query filter to be used by the corridor.</param> public PathCorridor(int maxPathSize, int maxCorners , NavmeshQuery query, NavmeshQueryFilter filter) { maxPathSize = Math.Max(1, maxPathSize); mRoot = PathCorridorEx.dtpcAlloc(maxPathSize); if (mRoot == IntPtr.Zero) { mMaxPathSize = 0; return; } mQuery = query; mFilter = filter; mMaxPathSize = maxPathSize; mCorners = new CornerData(Math.Max(2, maxCorners)); }
/// <summary> /// Loads the corridor data into the provided <see cref="PathCorridorData"/> buffer. /// </summary> /// <remarks> /// <para> /// Make sure the buffer is sized to hold the entire result. /// (See: <see cref="GetPathCount"/> and <see cref="MaxPathSize"/>.) /// </para> /// </remarks> /// <param name="buffer"> /// The buffer to load the data into. /// [Length: Maximum Path Size >= <see cref="GetPathCount"/>] /// </param> /// <returns>False if the operation failed.</returns> public bool GetData(PathCorridorData buffer) { // Only performs a partial parameter validation. if (buffer == null || buffer.path == null || buffer.path.Length < 1) { return(false); } if (buffer.path.Length == PathCorridorData.MarshalBufferSize) { return(PathCorridorEx.dtpcGetData(mRoot, buffer)); } buffer.pathCount = GetPath(buffer.path); buffer.position = mPosition.point; buffer.target = mTarget.point; return(true); }
/// <summary> /// Checks the corridor path to see if its polygon references remain valid. /// </summary> /// <remarks> /// <para> /// 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 resulting in it /// being filtered out. (E.g. An exclusion or inclusion flag changes.) /// </para> /// </remarks> /// <param name="maxLookAhead"> /// The number of polygons from the beginning of the corridor to search. /// </param> /// <returns>True if the seached portion of the path is still valid.</returns> public bool IsValid(int maxLookAhead) { return(PathCorridorEx.dtpcIsValid(mRoot, maxLookAhead, mQuery.root, mFilter.root)); }
/// <summary> /// The number of polygons in the corridor path. /// </summary> /// <returns>The number of polygons in the corridor path. /// </returns> public int GetPathCount() { return(PathCorridorEx.dtpcGetPathCount(mRoot)); }
/// <summary> /// Obtains a copy of the corridor path. /// </summary> /// <remarks> /// <para> /// The buffer should be sized to hold the entire path. /// (See: <see cref="GetPathCount"/> and <see cref="MaxPathSize"/>.) /// </para> /// </remarks> /// <param name="buffer">The buffer to load with the result. [(polyRef) * pathCount]</param> /// <returns>The number of polygons in the path.</returns> public int GetPath(uint[] buffer) { return(PathCorridorEx.dtpcGetPath(mRoot, buffer, buffer.Length)); }