将A *路径发现更改为HPA * - Java

时间:2014-08-05 10:24:30

标签: java path-finding a-star 2d-games

我为一个简单的基于2d网格的游戏实现了A *算法。然而,该算法减慢了游戏速度,我最近了解了HPA *,它试图在一定程度上尝试解决这个问题。

我无法找到任何对我有用的编码示例,因为我使用Java进行编码,而且我对从哪里开始调整初始代码感到困惑。

如果有人想看看,我的代码如下。我真的只是想以某种方式调整代码,以便将路径分割成网格,这样我就可以计算出一个可能是5个方形长度的路径,而不是20个方形长度的路径并且减慢游戏速度。

  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 :(得分:1)

让我们说:你实现一个类似爬行的游戏,并拥有一个迷宫,英雄和许多怪物......

在英雄让它移动之后

怪物转身,他们可能会移动,朝着英雄(一个*星)移动。我猜你在每一个回合计算每个怪物的路径?!?对?好的,然后出了点问题:

让每个怪物保持自己的道路。如果怪物距离很远,你就不必搜索最短的路径,因为即使你的英雄已经移动,最短的路径也大致相同(只有最后几步不同)。然后,也许如果怪物移动了10圈,你可以重新计算路径。通过这种方式,您可以将计算时间加速10倍 - 考虑到较长的路径比较短的路径需要更长的时间,您可以进行更多的优化

如果距离很远,可以每隔5步重新计算一次路径 如果它真的很接近它需要在每个回合重新计算路径,但不要担心......如果它是一个短路径,它不需要花费太多时间来计算!

抱歉 - 没有添加任何代码...

好的,让我们继续:这是一个简单的迷宫/地图:

simple maze

你从上/左开始想要走到尽头(不是A,不是B,你想走到尽头!)......

如果你不计算整个路径,你将永远找不到方法,因为所有其他路径似乎都更短,特别是当你在搜索中使用启发式时!

所以 - 没有办法不进行全路径搜索!

优化: 找到邻居通常是瓶颈:当你创建一个字段时,为每个字段设置邻居!

public class Test {

    public Field[][] field;
    public void doSomeTest(){

        field = new Field[25][25]
        MazeGenerator.createMaze(field); //this makes your map = this sets the fields passable or not
        for (int dy == 0; dy < 25; dy ++){
            for (int dx == 0; dx < 25; dx ++){
                createNeighbours(field[dx][dy]);                    
            }
        }

    }

    public void createNeigbours(Field f){

        //north
        if (isPassable(f.xpos, f.ypos-1) f.neighbours.add(field[xpos][ypos-1]); 

        //east
        if (isPassable(f.xpos+1, f.ypos) f.neighbours.add(field[xpos+1][ypos]);

        //south
        if (isPassable(f.xpos, f.ypos+1) f.neighbours.add(field[xpos][ypos+1]);

        //west
        if (isPassable(f.xpos-1, f.ypos) f.neighbours.add(field[xpos-1][ypos]);

    }

    public boolean isPassable(int xpos, int ypos){

        if (xpos <= 0) return false; //outside [maybe you even have a border, then xpos <= 1
        if (ypos <= 0) return false; //outside
        if (xpos >= Map.width) return false; //outside
        if (ypos >= Map.height) return false; //outside
        if (field[xpos][ypos].isBlocked) return false; //blocked

        return true;
    }
}

public class Field{

    public final int xpos;
    public final int ypos;
    public boolean isBlocked = false; // this makes your map

    public ArrayList<Field> neigbours = new ArrayList<Field>();

    public Field(int xpos, int ypos){
        this.xpos = xpos;
        this.ypos = ypos;
    }

    public List<Field> getNeighbours(){
        return neigbours ;
    }
}

上面的代码仅解释了如何在创建字段时创建邻居...但是如果展开节点(a *),则可以非常快速地获得邻居并且无需计算时间(想想创建对象的频率)在你的代码中)