summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs
diff options
context:
space:
mode:
authorchai <215380520@qq.com>2024-05-23 10:08:29 +0800
committerchai <215380520@qq.com>2024-05-23 10:08:29 +0800
commit8722a9920c1f6119bf6e769cba270e63097f8e25 (patch)
tree2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs
parent3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff)
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs283
1 files changed, 283 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs
new file mode 100644
index 0000000..0759e85
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Modifiers/StartEndModifier.cs
@@ -0,0 +1,283 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Pathfinding {
+ [System.Serializable]
+ /// <summary>
+ /// Adjusts start and end points of a path.
+ ///
+ /// This modifier is included in the <see cref="Pathfinding.Seeker"/> component and is always used if you are using a Seeker.
+ /// When a path is calculated the resulting path will only be the positions of the nodes it passes through.
+ /// However often you may not want to navigate to the center of a specific node but instead to a point on the surface of a node.
+ /// This modifier will adjust the endpoints of the path.
+ ///
+ /// [Open online documentation to see images]
+ /// </summary>
+ public class StartEndModifier : PathModifier {
+ public override int Order { get { return 0; } }
+
+ /// <summary>
+ /// Add points to the path instead of replacing them.
+ /// If for example <see cref="exactEndPoint"/> is set to ClosestOnNode then the path will be modified so that
+ /// the path goes first to the center of the last node in the path and then goes to the closest point
+ /// on the node to the end point in the path request.
+ ///
+ /// If this is false however then the relevant points in the path will simply be replaced.
+ /// In the above example the path would go directly to the closest point on the node without passing
+ /// through the center of the node.
+ /// </summary>
+ public bool addPoints;
+
+ /// <summary>
+ /// How the start point of the path will be determined.
+ /// See: <see cref="Exactness"/>
+ /// </summary>
+ public Exactness exactStartPoint = Exactness.ClosestOnNode;
+
+ /// <summary>
+ /// How the end point of the path will be determined.
+ /// See: <see cref="Exactness"/>
+ /// </summary>
+ public Exactness exactEndPoint = Exactness.ClosestOnNode;
+
+ /// <summary>
+ /// Will be called when a path is processed.
+ /// The value which is returned will be used as the start point of the path
+ /// and potentially clamped depending on the value of the <see cref="exactStartPoint"/> field.
+ /// Only used for the Original, Interpolate and NodeConnection modes.
+ /// </summary>
+ public System.Func<Vector3> adjustStartPoint;
+
+ /// <summary>
+ /// Sets where the start and end points of a path should be placed.
+ ///
+ /// Here is a legend showing what the different items in the above images represent.
+ /// The images above show a path coming in from the top left corner and ending at a node next to an obstacle as well as 2 different possible end points of the path and how they would be modified.
+ /// [Open online documentation to see images]
+ /// </summary>
+ public enum Exactness {
+ /// <summary>
+ /// The point is snapped to the position of the first/last node in the path.
+ /// Use this if your game is very tile based and you want your agents to stop precisely at the center of the nodes.
+ /// If you recalculate the path while the agent is moving you may want the start point snapping to be ClosestOnNode and the end point snapping to be SnapToNode however
+ /// as while an agent is moving it will likely not be right at the center of a node.
+ ///
+ /// [Open online documentation to see images]
+ /// </summary>
+ SnapToNode,
+ /// <summary>
+ /// The point is set to the exact point which was passed when creating the path request.
+ /// Note that if a path was for example requested to a point inside an obstacle, then the last point of the path will be inside that obstacle, which is usually not what you want.
+ /// Consider using the <see cref="Exactness.ClosestOnNode"/> option instead.
+ ///
+ /// [Open online documentation to see images]
+ /// </summary>
+ Original,
+ /// <summary>
+ /// The point is set to the closest point on the line between either the two first points or the two last points.
+ /// Usually you will want to use the NodeConnection mode instead since that is usually the behaviour that you really want.
+ /// This mode exists mostly for compatibility reasons.
+ /// [Open online documentation to see images]
+ /// Deprecated: Use NodeConnection instead.
+ /// </summary>
+ Interpolate,
+ /// <summary>
+ /// The point is set to the closest point on the surface of the node. Note that some node types (point nodes) do not have a surface, so the "closest point" is simply the node's position which makes this identical to <see cref="Exactness.SnapToNode"/>.
+ /// This is the mode that you almost always want to use in a free movement 3D world.
+ /// [Open online documentation to see images]
+ /// </summary>
+ ClosestOnNode,
+ /// <summary>
+ /// The point is set to the closest point on one of the connections from the start/end node.
+ /// This mode may be useful in a grid based or point graph based world when using the AILerp script.
+ ///
+ /// Note: If you are using this mode with a <see cref="Pathfinding.PointGraph"/> you probably also want to use the <see cref="Pathfinding.PointGraph.NodeDistanceMode Connection"/> for <see cref="PointGraph.nearestNodeDistanceMode"/>.
+ ///
+ /// [Open online documentation to see images]
+ /// </summary>
+ NodeConnection,
+ }
+
+ /// <summary>
+ /// Do a straight line check from the node's center to the point determined by the <see cref="Exactness"/>.
+ /// There are very few cases where you will want to use this. It is mostly here for
+ /// backwards compatibility reasons.
+ ///
+ /// Version: Since 4.1 this field only has an effect for the <see cref="Exactness"/> mode Original because that's the only one where it makes sense.
+ /// </summary>
+ public bool useRaycasting;
+ public LayerMask mask = -1;
+
+ /// <summary>
+ /// Do a straight line check from the node's center to the point determined by the <see cref="Exactness"/>.
+ /// See: <see cref="useRaycasting"/>
+ ///
+ /// Version: Since 4.1 this field only has an effect for the <see cref="Exactness"/> mode Original because that's the only one where it makes sense.
+ /// </summary>
+ public bool useGraphRaycasting;
+
+ List<GraphNode> connectionBuffer;
+ System.Action<GraphNode> connectionBufferAddDelegate;
+
+ public override void Apply (Path _p) {
+ var p = _p as ABPath;
+
+ // This modifier only supports ABPaths (doesn't make much sense for other paths anyway)
+ if (p == null || p.vectorPath.Count == 0) return;
+
+ bool singleNode = false;
+
+ if (p.vectorPath.Count == 1 && !addPoints) {
+ // Duplicate first point
+ p.vectorPath.Add(p.vectorPath[0]);
+ singleNode = true;
+ }
+
+ // Add instead of replacing points
+ bool forceAddStartPoint, forceAddEndPoint;
+ // Which connection the start/end point was on (only used for the Connection mode)
+ int closestStartConnection, closestEndConnection;
+
+ Vector3 pStart = Snap(p, exactStartPoint, true, out forceAddStartPoint, out closestStartConnection);
+ Vector3 pEnd = Snap(p, exactEndPoint, false, out forceAddEndPoint, out closestEndConnection);
+
+ // This is a special case when the path is only a single node and the Connection mode is used.
+ // (forceAddStartPoint/forceAddEndPoint is only used for the Connection mode)
+ // In this case the start and end points lie on the connections of the node.
+ // There are two cases:
+ // 1. If the start and end points lie on the same connection we do *not* want
+ // the path to pass through the node center but instead go directly from point to point.
+ // This is the case of closestStartConnection == closestEndConnection.
+ // 2. If the start and end points lie on different connections we *want*
+ // the path to pass through the node center as it goes from one connection to another one.
+ // However in any case we only want the node center to be added once to the path
+ // so we set forceAddStartPoint to false anyway.
+ if (singleNode) {
+ if (closestStartConnection == closestEndConnection) {
+ forceAddStartPoint = false;
+ forceAddEndPoint = false;
+ } else {
+ forceAddStartPoint = false;
+ }
+ }
+
+ // Add or replace the start point
+ // Disable adding of points if the mode is SnapToNode since then
+ // the first item in vectorPath will very likely be the same as the
+ // position of the first node
+ if ((forceAddStartPoint || addPoints) && exactStartPoint != Exactness.SnapToNode) {
+ p.vectorPath.Insert(0, pStart);
+ } else {
+ p.vectorPath[0] = pStart;
+ }
+
+ if ((forceAddEndPoint || addPoints) && exactEndPoint != Exactness.SnapToNode) {
+ p.vectorPath.Add(pEnd);
+ } else {
+ p.vectorPath[p.vectorPath.Count-1] = pEnd;
+ }
+ }
+
+ Vector3 Snap (ABPath path, Exactness mode, bool start, out bool forceAddPoint, out int closestConnectionIndex) {
+ var index = start ? 0 : path.path.Count - 1;
+ var node = path.path[index];
+ var nodePos = (Vector3)node.position;
+
+ closestConnectionIndex = 0;
+
+ forceAddPoint = false;
+
+ switch (mode) {
+ case Exactness.ClosestOnNode:
+ return start ? path.startPoint : path.endPoint;
+ case Exactness.SnapToNode:
+ return nodePos;
+ case Exactness.Original:
+ case Exactness.Interpolate:
+ case Exactness.NodeConnection:
+ Vector3 relevantPoint;
+ if (start) {
+ relevantPoint = adjustStartPoint != null? adjustStartPoint() : path.originalStartPoint;
+ } else {
+ relevantPoint = path.originalEndPoint;
+ }
+
+ switch (mode) {
+ case Exactness.Original:
+ return GetClampedPoint(nodePos, relevantPoint, node);
+ case Exactness.Interpolate:
+ // Adjacent node to either the start node or the end node in the path
+ var adjacentNode = path.path[Mathf.Clamp(index + (start ? 1 : -1), 0, path.path.Count-1)];
+ return VectorMath.ClosestPointOnSegment(nodePos, (Vector3)adjacentNode.position, relevantPoint);
+ case Exactness.NodeConnection:
+ // This code uses some tricks to avoid allocations
+ // even though it uses delegates heavily
+ // The connectionBufferAddDelegate delegate simply adds whatever node
+ // it is called with to the connectionBuffer
+ connectionBuffer = connectionBuffer ?? new List<GraphNode>();
+ connectionBufferAddDelegate = connectionBufferAddDelegate ?? (System.Action<GraphNode>)connectionBuffer.Add;
+
+ // Adjacent node to either the start node or the end node in the path
+ adjacentNode = path.path[Mathf.Clamp(index + (start ? 1 : -1), 0, path.path.Count-1)];
+
+ // Add all neighbours of #node to the connectionBuffer
+ node.GetConnections(connectionBufferAddDelegate);
+ var bestPos = nodePos;
+ var bestDist = float.PositiveInfinity;
+
+ // Loop through all neighbours
+ // Do it in reverse order because the length of the connectionBuffer
+ // will change during iteration
+ for (int i = connectionBuffer.Count - 1; i >= 0; i--) {
+ var neighbour = connectionBuffer[i];
+ if (!path.CanTraverse(neighbour)) continue;
+
+ // Find the closest point on the connection between the nodes
+ // and check if the distance to that point is lower than the previous best
+ var closest = VectorMath.ClosestPointOnSegment(nodePos, (Vector3)neighbour.position, relevantPoint);
+
+ var dist = (closest - relevantPoint).sqrMagnitude;
+ if (dist < bestDist) {
+ bestPos = closest;
+ bestDist = dist;
+ closestConnectionIndex = i;
+
+ // If this node is not the adjacent node
+ // then the path should go through the start node as well
+ forceAddPoint = neighbour != adjacentNode;
+ }
+ }
+
+ connectionBuffer.Clear();
+ return bestPos;
+ default:
+ throw new System.ArgumentException("Cannot reach this point, but the compiler is not smart enough to realize that.");
+ }
+ default:
+ throw new System.ArgumentException("Invalid mode");
+ }
+ }
+
+ protected Vector3 GetClampedPoint (Vector3 from, Vector3 to, GraphNode hint) {
+ Vector3 point = to;
+ RaycastHit hit;
+
+ if (useRaycasting && Physics.Linecast(from, to, out hit, mask)) {
+ point = hit.point;
+ }
+
+ if (useGraphRaycasting && hint != null) {
+ var rayGraph = AstarData.GetGraph(hint) as IRaycastableGraph;
+
+ if (rayGraph != null) {
+ GraphHitInfo graphHit;
+ if (rayGraph.Linecast(from, point, out graphHit)) {
+ point = graphHit.point;
+ }
+ }
+ }
+
+ return point;
+ }
+ }
+}