Java游戏服务器开发之A星算法
   学习这个主要是用于寻路算法。
   代码也是大部分参考里面,原本是C#的,用Java实现了一遍。
   一般看视频的话,基本一知半解,自己敲一遍,基本就能了解了
   
我先直接放代码,先跑成功后,再说明下整个实现
首先,需要将我们的地图虚拟化,我们用Point表示。
其次,创建整个地图,按照长宽来初始化。initMap
接着,写出计算FGH的值(先不用管是什么)的函数。calcF
再者,就是书写查找路径的方法。 findPath
1.循环"开启列表",while (openList.size() > 0) 
2.找到列表中F值最小的点M。findMinFOfPoint
3.找到M点周边可以到达的点数组L。getSurroundPoints
4.使用相关条件过滤掉一部分L中的点。pointsFilter
5.循环L,判断里面的G值是否比原来的更小(更优)for (Point surroundPoint : surroundPoints)

最后,就是输出结果showLoad

看具体的代码

Point类

                    

package com.lizhao.astar.siki;

public class Point implements Comparable<Point> {
  private Point parent;
  private int x;
  private int y;

  private float F;
  private float G;
  private float H;

  private boolean isWall = false;
  private boolean isLoad = false;

  public Point(int x, int y) {
    this(x, y, null);
  }

  public Point(int x, int y, Point parent) {
    this.x = x;
    this.y = y;
    this.parent = parent;
  }

  public void UpdateParent(Point parent, float g) {
    this.parent = parent;
    this.G = g;
    F = G + H;
  }

  public void updateFGH(float f, float g, float h) {
    this.F = f;
    this.G = g;
    this.H = h;
  }

  public Point getParent() {
    return parent;
  }

  public void setParent(Point parent) {
    this.parent = parent;
  }

  public int getX() {
    return x;
  }

  public void setX(int x) {
    this.x = x;
  }

  public int getY() {
    return y;
  }

  public void setY(int y) {
    this.y = y;
  }

  public float getF() {
    return F;
  }

  public void setF(float f) {
    F = f;
  }

  public float getG() {
    return G;
  }

  public void setG(float g) {
    G = g;
  }

  public float getH() {
    return H;
  }

  public void setH(float h) {
    H = h;
  }

  public boolean isWall() {
    return isWall;
  }

  public void setWall(boolean wall) {
    isWall = wall;
  }

  public boolean isLoad() {
    return isLoad;
  }

  public void setLoad(boolean load) {
    isLoad = load;
  }

  @Override
  public int compareTo(Point point) {
    return Float.compare(F, point.getF());
  }

  @Override
  public String toString() {
    return "Point " + (getX() + 1) + "," + (getY() + 1);
  }
}



initMap方法

                    



public void initMap() {
    for (int x = 0; x < mapWith; x++) {
      for (int y = 0; y < mapHeight; y++) {
        map[x][y] = new Point(x, y);
      }
    }
//    map[6][3].setWall(true);
    map[6][4].setWall(true);
    map[6][5].setWall(true);
    map[6][6].setWall(true);
    //    map[4][5].setWall(true);
    //    map[4][6].setWall(true);
  }

calcF方法

/*************************************************************
   *第一步,写出计算FGH的值
   **********************************************************/
  /**
   * 计算F值
   *
   * @param now 当前点
   * @param end 终点
   */
  private void calcF(Point now, Point end) {
    //F = G + H
    // H 表示从指定的方格移动到终点 B 的预计耗费 (H 有很多计算方法, 这里我们设定只可以
    //上下左右移动).
    //G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).
    float h = calcH(now, end);
    float g = calcG(now, now.getParent());
    float f = g + h;
    now.updateFGH(f, g, h);

  }

  /**
   * 获取H值
   * H 表示从指定的方格移动到终点 B 的预计耗费
   * (H 有很多计算方法, 这里我们设定只可以上下左右移动).
   *
   * @param start
   * @param end
   * @return
   */
  private float calcH(@NotNull Point start, @NotNull Point end) {
    return Math.abs(end.getX() - start.getX()) + Math.abs(end.getY() - start.getY());
  }

  /**
   * 获取G值
   * G 表示从起点 A 移动到网格上指定方格的移动耗费 (可沿斜方向移动).
   * 父节点到走到该节点的消耗
   *
   * @param start
   * @param parent
   * @return
   */
  private float calcG(@NotNull Point start, @NotNull Point parent) {
    float g = 0;
    if (start.getParent() == null) {
      g = 0;
    } else {
      g = PointUtil.getDistance(start, parent) + parent.getG();
    }
    return g;
  }



findPath方法


findMinFOfPoint 方法

/**
   * 开启列表中F值最小的点
   *
   * @param openList
   * @return
   */
  private Point findMinFOfPoint(List<Point> openList) {
    float f = Float.MAX_VALUE;
    Point temp = null;
    for (Point p : openList) {
      if (p.getF() < f) {
        temp = p;
        f = p.getF();
      }
    }
    return temp;
  }



getSurroundPoints 方法、

/**
   * 周围的点
   *
   * @param point
   * @return
   */
  private List<Point> getSurroundPoints(Point point) {
    Point up = null, down = null, left = null, right = null;
    Point lu = null, ru = null, ld = null, rd = null;
    if (point.getY() < mapHeight - 1) {
      up = map[point.getX()][point.getY() + 1];
    }
    if (point.getY() > 0) {
      down = map[point.getX()][point.getY() - 1];
    }
    if (point.getX() > 0) {
      left = map[point.getX() - 1][point.getY()];
    }
    if (point.getX() < mapWith - 1) {
      right = map[point.getX() + 1][point.getY()];
    }
    if (up != null && left != null) {
      lu = map[point.getX() - 1][point.getY() + 1];
    }
    if (up != null && right != null) {
      ru = map[point.getX() + 1][point.getY() + 1];
    }
    if (down != null && left != null) {
      ld = map[point.getX() - 1][point.getY() - 1];
    }
    if (down != null && right != null) {
      rd = map[point.getX() + 1][point.getY() - 1];
    }
    List<Point> list = new ArrayList<Point>();
    if (down != null && down.isWall() == false) {
      list.add(down);
    }
    if (up != null && up.isWall() == false) {
      list.add(up);
    }
    if (left != null && left.isWall() == false) {
      list.add(left);
    }
    if (right != null && right.isWall() == false) {
      list.add(right);
    }
    if (lu != null && lu.isWall() == false && left.isWall() == false && up.isWall() == false) {
      list.add(lu);
    }
    if (ld != null && ld.isWall() == false && left.isWall() == false && down.isWall() == false) {
      list.add(ld);
    }
    if (ru != null && ru.isWall() == false && right.isWall() == false && up.isWall() == false) {
      list.add(ru);
    }
    if (rd != null && rd.isWall() == false && right.isWall() == false && down.isWall() == false) {
      list.add(rd);
    }
    return list;
  }



pointsFilter 方法

/**
   * 过滤掉周围的点
   *
   * @param src
   * @param closeList
   */
  private void pointsFilter(List<Point> src, List<Point> closeList) {
    for (Point p : closeList) {
      if (src.indexOf(p) > -1) {
        src.remove(p);
      }
    }
  }



                        findPath具体

private void findPath(Point start, Point end) {

    List<Point> openList = new ArrayList<Point>();
    List<Point> closeList = new ArrayList<Point>();
    openList.add(start);
    while (openList.size() > 0) {
      Point point = findMinFOfPoint(openList);
      openList.remove(point);
      closeList.add(point);
      List<Point> surroundPoints = getSurroundPoints(point);
      pointsFilter(surroundPoints, closeList);

      for (Point surroundPoint : surroundPoints) {
        if (openList.indexOf(surroundPoint) > -1) {
          float nowG = calcG(surroundPoint, point);
          if (nowG < surroundPoint.getG()) {
            surroundPoint.UpdateParent(point, nowG);
          }
        } else {
          surroundPoint.setParent(point);
          calcF(surroundPoint, end);
          openList.add(surroundPoint);
        }
      }
      //判断一下
      if (openList.indexOf(end) > -1) {
        break;
      }
    }
  }



showLoad方法


private void showLoad() {
    for (int i = 0; i < map.length; i++) {
      for (int j = 0; j < map[i].length; j++) {
        if (map[i][j].isLoad()) {
          System.out.print("$");
        } else if (map[i][j].isWall()) {
          System.out.print(1);
        } else if (map[i][j].getParent() != null) {
          System.out.print(3);
        } else {
          System.out.print(0);
        }
        System.out.print(",");
      }
      System.out.println();
    }
  }

写个测试方法:

    

//测试A星算法
  public static void testAStar() {
    AStar aStar = new AStar();

    aStar.initMap();
    Point start = aStar.map[4][4];
    Point end = aStar.map[8][5];

    aStar.findPath(start, end);
    aStar.showPath(start, end);
    aStar.showLoad();
    System.out.println("Hello World! ");
  }



最后的输出结果,0是地图,1是墙,3是被探索过的区域,$是具体的路径

0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,3,3,0,0,0,0,0,0,0,0,0,
	0,0,3,3,3,3,1,0,0,0,0,0,0,0,0,
6	0,0,3,3,3,3,1,3,$,3,0,0,0,0,0,
5	0,0,3,3,$,3,1,3,$,3,0,0,0,0,0,
4	0,0,0,3,3,$,$,$,3,3,0,0,0,0,0,
	0,0,0,0,3,3,3,3,3,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
			5,6	
			
			6,4		14+50
			6,5		10+40
			6,6		14+30
			6,7		24+40





nice
上面的代码在码云上 https://gitee.com/lizhaoandroid/BehaviorTree,在com.lizhao.astar.siki包下面