Пример #1
0
        /// <summary>
        /// Neighbour handling of our AStar algorithm.
        /// </summary>
        private void HandleNeighbours(AStarNode current, List<MeshNode> neighbours, MinHeap<AStarNode> workingQueue, PointSet<float> workingCoords, PointSet<float> exploredCoords, Vector2 end)
        {
            foreach (MeshNode neighbour in neighbours)
            {

                // neighbour will be added if neither in workingQueue(workingCoords) nor exploredCoords
                if (!exploredCoords.Contains(neighbour.mVector.X, neighbour.mVector.Y) && !workingCoords.Contains(neighbour.mVector.X, neighbour.mVector.Y))
                {
                    double neighbourCostsToEnd = HelperMethods.EuklidDistance(neighbour.mVector, end);
                    double neighbourLatestCosts =
                        current.mLatestCosts + HelperMethods.EuklidDistance(current.mNode.mVector, neighbour.mVector);
                    AStarNode neighbour2 = new AStarNode(neighbourLatestCosts, neighbourCostsToEnd, neighbour, current);

                    // add neighbour with its heuristic value to workingQueue:
                    workingQueue.Add(HeuristicValue(neighbour2), neighbour2);
                    workingCoords.Add(neighbour.mVector.X, neighbour.mVector.Y);
                }
            }
        }
Пример #2
0
 /// <summary>
 /// Returns the heuristic value for a node. In our case
 /// it is just its latest costs plus the air distance to the target.
 /// </summary>
 /// <param name="current">node of which the value should be returned</param>
 /// <returns>heuristic value of 'current'</returns>
 private double HeuristicValue(AStarNode current)
 {
     return current.mLatestCosts + current.mCostsToEnd;
 }
Пример #3
0
        /// <summary>
        /// Prepares the result for CalculatePath2().
        /// </summary>
        private Path HandleEnding(AStarNode current, Vector2 end, bool startPassable, bool endPassable, Polygon endPolygon)
        {
            // we're done!
            AStarNode last;
            if (endPassable && !current.mNode.mVector.Equals(end))
                // We really want to get to the end point!
                last = new AStarNode(0, 0, new MeshNode(end), current);
            else
                // We don't need to reach the end point
                last = current;

            // path cleanup needed?
            if (GlobalValues.GetInstance().mCleanUpPath)
                return PathCleaner.GetCleanedUpVectorPath(last, startPassable, endPolygon);
            /*else*/
                return GetPath(last, startPassable);
        }
Пример #4
0
 /// <summary>
 /// Returns the non-cleaned-up path.
 /// </summary>
 /// <param name="target">the last end AStarNode</param>
 /// <param name="startPassable">if start is passable</param>
 private Path GetPath(AStarNode target, bool startPassable)
 {
     Stack<Vector2> stack = new Stack<Vector2>();
     while (target != null)
     {
         stack.Push(target.mNode.mVector);
         target = target.mPrecedent;
     }
     // If we don't need the start point, we just pop the most recent point:
     if (!startPassable) stack.Pop();
     return new Path(stack);
 }
Пример #5
0
 /// <summary>
 /// Checks the end condition of our AStar algorithm.
 /// </summary>
 private bool EndConditionMet(AStarNode current, bool endPassable, Polygon endPolygon, List<MeshNode> endNodes)
 {
     if (endPassable)
     {   // use endPolygon to determine termination
         // we're done if the current node is part of the endPolygon
         return (current.mNode.mPolyOwners.Contains(endPolygon));
     }
     /*else*/
     {   // use endNodes to determine termination
         // we're done if the current node is part of endNodes:
         return (endNodes.Contains(current.mNode));
     }
 }
Пример #6
0
        /// <summary>
        /// This does the actual work for CalculatePath(). CalculatePath() did just
        /// determine all the parameters for it.
        /// </summary>
        private Path CalculatePath2(Vector2 end, MeshNode startMeshNode, List<MeshNode> firstNeighbours, Polygon endPolygon, List<MeshNode> endNodes, bool startPassable, bool endPassable)
        {
            Debug.Assert(mMesh != null, "There is no navigation mesh! Load a map and then call PathFinder.GetInstance().LoadMeshFromObstructions().");
            // uses A* algorithm
            // !! remember to distinguish between latest costs (costs up to that point)
            // !! and heuristic costs (estimated path costs over that point to target)
            // I use AStarNode instead of MeshNode / Vector2 because I have to save
            // additional information, like predecessor and latest costs.
            // WorkingQueue should be in ascending order
            // of heuristic value. I use my own comparer to allow multiple
            // equal keys (since we may have nodes with the same heuristic value).
            MinHeap<AStarNode> workingQueue = new MinHeap<AStarNode>();
            // workingQueue doesn't support a quick check if some coords are
            // already contained, so we handle that with a corresp. structure:
            PointSet<float> workingCoords = new PointSet<float>();
            PointSet<float> exploredCoords = new PointSet<float>();

            // TODO: Evaluate First Loop iteration separately
            double startCostsToEnd = HelperMethods.EuklidDistance(startMeshNode.mVector, end);
            AStarNode current = new AStarNode(0, startCostsToEnd, startMeshNode, null);
            if (EndConditionMet(current, endPassable, endPolygon, endNodes))
                return HandleEnding(current, end, startPassable, endPassable, endPolygon);
            exploredCoords.Add(startMeshNode.mVector.X, startMeshNode.mVector.Y);
            HandleNeighbours(current, firstNeighbours, workingQueue, workingCoords, exploredCoords, end);
            // TODO: First iteration evaluation finished

            while (workingQueue.Count > 0)
            {
                //Debug.WriteLine("No. of nodes in the workingQueue: "+workingQueue.Count);
                //OutputHeuristics(workingQueue);
                current = GetNextNodeFromWorkingQueue(workingQueue, workingCoords);

                //Debug.WriteLine("Now examining "+current.mNode.mVector.X+","+current.mNode.mVector.Y);
                if (EndConditionMet(current, endPassable, endPolygon, endNodes))
                    return HandleEnding(current, end, startPassable, endPassable, endPolygon);

                // The shortest path to current has been found,
                // so we won't have to touch it ever again!
                exploredCoords.Add(current.mNode.mVector.X, current.mNode.mVector.Y);
                //Debug.WriteLine("Explored " + current.mNode.mVector.X + "," + current.mNode.mVector.Y);

                // Add all unexplored unblocked neighbours to workingQueue:
                List<MeshNode> neighbours = GetNeighbours2(current.mNode);
                HandleNeighbours(current, neighbours, workingQueue, workingCoords, exploredCoords, end);
            }

            // Working queue got empty, but we still didn't reach our target
            // -> No path found
            Debug.WriteLine("WARNING - Pathfinder: No path found!");
            return null;
        }
Пример #7
0
        /// <summary>
        /// Takes the result from PathFinder.CalculatePath() and returns a cleaned
        /// up path from it.
        /// </summary>
        public static Path GetCleanedUpVectorPath(AStarNode target, bool startPassable, Polygon endPolygon)
        {
            // First get the list of meshNodes:
            List<MeshNode> meshNodes = new List<MeshNode>();
            while (target != null)
            {
                meshNodes.Add(target.mNode);
                target = target.mPrecedent;
            }
            // If we don't need to reach the start point, we just remove the most recent point:
            if (!startPassable) meshNodes.RemoveAt(meshNodes.Count - 1);
            meshNodes.Reverse();

            // We're ready to kick ass!
            if (meshNodes.Count > 2)
            {   // We can only clean up with 3 or more nodes
                int uniteFromIndex = 0;
                while (uniteFromIndex < meshNodes.Count - 2)
                {
                    Debug.Assert(uniteFromIndex < meshNodes.Count - 2);

                    int i = uniteFromIndex + 2;
                    Debug.Assert(i < meshNodes.Count);

                    Stack<Edge> firstCandidateEdges = new Stack<Edge>(meshNodes[uniteFromIndex].GetPolyOwnersEdges());
                    Debug.Assert(firstCandidateEdges.Count > 0,
                                 "GetCleanedUpVectorPath(): Could not find first candidate edges!");

                    Debug.Assert(uniteFromIndex < i - 1);
                    Debug.Assert(uniteFromIndex < meshNodes.Count);
                    Debug.Assert(i < meshNodes.Count);

                    while (i < meshNodes.Count && IsCleanUpPossible(GetCleanUpEndPolygons(i, meshNodes, endPolygon),
                                                                    firstCandidateEdges,
                                                                    meshNodes[uniteFromIndex].mVector,
                                                                    meshNodes[i].mVector))
                    {
                        i++;
                    }
                    // Whatever case has broken the loop, we try to
                    // clean up from uniteFromIndex to i-1 if feasible:
                    i--;
                    if (uniteFromIndex < i - 1)
                    {
                        int removedNodeCount = i - uniteFromIndex - 1;
                        Debug.Assert(removedNodeCount > 0);
                        CleanUp(uniteFromIndex, i, meshNodes);
                    }
                    uniteFromIndex++;
                }
                // uniteFromIndex is too high to clean up anymore
            }

            // We're done:
            Stack<Vector2> stack = new Stack<Vector2>();
            // fill path backwards (it's a stack after all):
            for (int j = meshNodes.Count - 1; j >= 0; j--)
            {
                stack.Push(meshNodes[j].mVector);
            }
            return new Path(stack);
        }