Java AStar寻路 - 在多个段中创建一个路径

时间:2014-07-31 10:44:26

标签: java path-finding a-star

我有一个网格,我正在使用一个星星,在绕过障碍物的同时可以成功地从一个方格到另一个方格。但是,计算一个长时间会导致一定的延迟,所以我想一次不是一次计算一个20格长的路径,为什么不创建前5个步骤然后在那个点创建接下来的5个步骤,依此类推。

我的问题是,你不期望代理人仍然遵循相同的路线而不管一次计算5个步骤吗?目前,如果我将其改为一次5个步骤,那么它将开始采用不同的路径并且它将很快崩溃(这个问题就在这里,它更像是我需要弄清楚的不同路径)。

以下是我在网上找到的修改后的路径查找代码。我不确定它会有多么有用,并且还有其他部分的代码显然可以使用它,我认为它太多了。需要注意的重要部分是注释掉的行“// || shortestPath.getWayPointPath()。size()> = 5”,以便何时判断是否已创建列表。

以下示例。通常情况下,0将直接进入墙壁及其周围,但是当我尝试以任何方式修改我的路径查找时,0将直接直接到地图的底部然后向右。对于任何一张地图来说,它似乎都想让它走近目标的最长路线,我无法理解为什么,是否有人有任何想法我可能做错了或更好的方法来解决这个问题?

xxxxxxxxxxxxxxxxx
0    x      1
     x
     x
     x

我的寻路段代码在

之下
        if (shortestPath == null) {

         openLocations.add(playerLocation);

            // While the goal has not been found yet
            while (openLocations.size() != 0 || pathFound != true) {
                // get the first node from the open list
                Node current = openLocations.get(0);
                shortestPath = reconstructPath(current);

                // check if current node is the goal node
                if (current.getX() == goalLocation.getX()
                        && current.getY() == goalLocation.getY()
                        //|| shortestPath.getWayPointPath().size() > GameInfo.getPathLength() + GameInfo.getPathCounter()
                        //|| shortestPath.getWayPointPath().size() >= 5
                        ) {
                    shortestPath = reconstructPath(current);
                    pathFound = true;

                    for(Node node: shortestPath.getWayPointPath())
                     totalClosedLocations.add(node);

                    // path has been found
                    break;
                }

                // move current node to the already searched (closed) list
                openLocations.remove(current);
                closedLocations.add(current);

                // set the current nodes neighbours
                current = setNeighbours(current);

                // Now it's time to go through all of the current nodes
                // neighbours and see if they should be the next step
                for (Node neighbor : current.getNeighborList()) {
                    boolean neighborIsBetter;

                    // if we have already searched this Node, don't bother and
                    // continue to the next one
                    if (closedLocations.contains(neighbor)) {
                        continue;
                    }

                    boolean found = false;
                    for (Node neighbournode : closedLocations) {
                        if (neighbournode.getX() == neighbor.getX()
                                && neighbournode.getY() == neighbor.getY()) {
                            found = true;
                            continue;
                        }
                    }

                    if (found)
                        continue;

                    Node movable = new Node(neighbor.getX(), neighbor.getY(), 
                            neighbor.getCategory(), neighbor.getItype(), neighbor.getId());

                    if (grid[movable.getX()][movable.getY()].size() > 0) {
                        // check to make sure that the square is not of category
                        // 4(immovable object) or category 3(enemy)
                        if ((grid[movable.getX()][movable.getY()].get(0).category == 4 && grid[movable
                                .getX()][movable.getY()].get(0).itype == 0)
                                && grid[movable.getX()][movable.getY()].get(0).obsID != goalLocation.getId()
                                ) {
                            // You cannot move on this square
                            neighbor.setMoveable(false);
                        } else {
                            // You can move on this square. Set parent location
                            // as the players current position.
                            movable.setParent(playerLocation);
                        }
                    }

                    // also just continue if the neighbor is an obstacle
                    if (neighbor.getMoveable()) {

                        // calculate how long the path is if we choose this
                        // neighbor
                        // as the next step in the path
                        float neighborDistanceFromStart = (current
                                .getDistanceFromStart() + getDistanceBetween(
                                current, neighbor));

                        // add neighbor to the open list if it is not there
                        if (!openLocations.contains(neighbor)) {
                            openLocations.add(neighbor);
                            neighborIsBetter = true;
                            // if neighbor is closer to start it could also be
                            // better
                        } else if (neighborDistanceFromStart < current
                                .getDistanceFromStart()) {
                            neighborIsBetter = true;
                        } else {
                            neighborIsBetter = false;
                        }
                        // set neighbors parameters if it is better
                        if (neighborIsBetter) {
                            neighbor.setParent(current);
                            neighbor.setDistanceFromStart(neighborDistanceFromStart);
                            neighbor.setHeuristicDistanceFromGoal(heuristicStar
                                    .getEstimatedDistanceToGoal(
                                            neighbor.getX(), neighbor.getY(),
                                            goalLocation.getX(),
                                            goalLocation.getY()));
                        }
                    }

                }
            }

            System.out.println("====================");
        }

1 个答案:

答案 0 :(得分:0)

除了获得完整路径而不是独立地计算路径的几个阶段之外的原因是为了保证解的最优性。除此之外,我认为通过获得~200个单元长度的路径不应该遇到性能问题。也许你的代码有问题。我认为使用提供A *实现的搜索库会更容易。我建议你看看Hipster library。该库的结构非常友好,并且将不同的组件与算法的主要逻辑分开,从而产生非常干净,模块化和可扩展的代码。以下是使用Hipster解决2D路径规划的示例代码:

//HERE YOU DEFINE THE SEARCH PROBLEM
// The maze is a 2D map, where each tile defined by 2D coordinates x and y
// can be empty or occupied by an obstacle. We have to define de transition
// function that tells the algorithm which are the available movements from
// a concrete tile point.
SearchProblem p = ProblemBuilder.create()
   .initialState(origin)
   .defineProblemWithoutActions()
   .useTransitionFunction(new StateTransitionFunction<Point>() {
      @Override
      public Iterable<Point> successorsOf(Point state) {
         // The transition function returns a collection of transitions.
         // A transition is basically a class Transition with two attributes:
         // source point (from) and destination point (to). Our source point
         // is the current point argument. We have to compute which are the
         // available movements (destination points) from the current point.
         // Class Maze has a helper method that tell us the empty points
         // (where we can move) available:
         //TODO: FILL WITH YOUR CODE GENERATING THE NEIGHBORS, FILTERING
         //THOSE WHICH ARE NOT ACCESIBLE DUE TO OBSTACLES IN YOUR MAP
        return [...]
      }
   })
   .useCostFunction(new CostFunction<Void, Point, Double>() {
      // We know now how to move (transitions) from each tile. We need to define the cost
      // of each movement. A diagonal movement (for example, from (0,0) to (1,1)) is longer
      // than a top/down/left/right movement. Although this is straightforward, if you don't
      // know why, read this http://www.policyalmanac.org/games/aStarTutorial.htm.
      // For this purpose, we define a CostFunction that computes the cost of each movement.
      // The CostFunction is an interface with two generic types: S - the state, and T - the cost
      // type. We use Points as states in this problem, and for example doubles to compute the distances:
      @Override
      public Double evaluate(Transition<Void, Point> transition) {
         Point source = transition.getFromState();
         Point destination = transition.getState();
         // The distance from the source to de destination is the euclidean
         // distance between these two points http://en.wikipedia.org/wiki/Euclidean_distance
         return source.distance(destination);
      }
   })
   .build();

//HERE YOU INSTANTIATE THE ALGORITHM AND EXECUTE THE SEARCH
//MazeSearch.printSearch(Hipster.createAStar(p).iterator(), maze);
System.out.println(Hipster.createAStar(p).search(goal));

(完整代码here

该库已获得Apache2许可,您可以在Github repository中查看示例,并根据您的情况调整转换功能。您会发现该库对您的问题非常有用。如果您对如何生成点的邻居(过滤由于地图中的障碍而无法访问的那些)有疑问,请查看已实现in the class Maze2D of the library的函数validLocationsFrom(Point)

/**
* Return all neighbor empty points from a specific location point.
* @param loc source point
* @return collection of empty neighbor points.
*/
public Collection<Point> validLocationsFrom(Point loc) {
   Collection<Point> validMoves = new HashSet<Point>();
   // Check for all valid movements
   for (int row = -1; row <= 1; row++) {
      for (int column = -1; column <= 1; column++) {
         try {
            if (isFree(new Point(loc.x + column, loc.y + row))) {
               validMoves.add(new Point(loc.x + column, loc.y + row));
            }
         } catch (ArrayIndexOutOfBoundsException ex) {
            // Invalid move!
         }
      }
   }
   validMoves.remove(loc);
   return validMoves;
}

此外,如果您关注性能,A *的这种实现应该在几毫秒内找到最佳路径。该算法以动态方式生成搜索算法使用的节点,在大问题中节省了大量内存。

我希望我的答案有所帮助,