package com.laotou99.astar;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Panel;
import java.awt.Point;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.awt.image.ImageProducer;
import java.io.Serializable;
import java.util.List;
//Java的A*寻径实现
public class Main extends Panel implements Serializable
{
private static final long serialVersionUID = 1L;
final static private int WIDTH = 480;
final static private int HEIGHT = 480;
final static private int CS = 32;
private TestMap map;
private PathFinder astar;
// 起始坐标1,1
private static Point START_POS = new Point(1,1);
// 目的坐标10,13
private static Point OBJECT_POS = new Point(7,7);
private BufferedImage screen;
private Graphics graphics;
private List path;
public Main(){
setSize(WIDTH,HEIGHT);
setFocusable(true);
screen = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB);
graphics = screen.getGraphics();
map = new TestMap();
// 注入地图描述及障碍物描述
astar = new PathFinder(TestMap.MAP, TestMap.HIT);
// searchPath将获得两点间移动路径坐标的List集合
// 在实际应用中,利用Thread分步处理List中坐标即可实现自动行走
path = astar.searchPath(START_POS, OBJECT_POS);
}
@Override
public void update(Graphics g) {
paint(g);
}
@Override
public void paint(Graphics g) {
// 绘制地图
map.draw(graphics);
graphics.setColor(Color.RED);
graphics.fillRect(START_POS.x*CS, START_POS.y*CS, CS, CS);
graphics.setColor(Color.BLUE);
graphics.fillRect(OBJECT_POS.x*CS, OBJECT_POS.y*CS, CS, CS);
// 绘制路径
if(path!=null){
graphics.setColor(Color.YELLOW);
//遍历坐标,并一一描绘
for(int i=0;i<path.size();i++){
Node node = (Node)path.get(i);
Point pos = node._pos;
// 描绘边框
graphics.fillRect(pos.x*CS+7, pos.y*CS+7, CS-14,CS-14);
}
}
g.drawImage(screen, 0, 0, this);
}
public static void main(String[] args) {
Frame frame = new Frame();
frame.setTitle("Java的爱心 寻径实现");
frame.setSize(WIDTH, HEIGHT);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.add(new Main());
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e){
System.exit(0);
}
});
}
}
package com.laotou99.astar;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
//一个简单的地图实现
public class TestMap {
final static private int CS = 32;
// 地图描述
final static public int[][] MAP = {
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 } };
// 无法移动区域
final static public int[] HIT={1};
// 设定背景方格默认行数
final static private int ROW = 15;
// 设定背景方格默认列数
final static private int COL = 15;
public TestMap(){ }
public void draw(Graphics g){
for(int i=0;i<ROW;i++){
for(int j=0;j<COL;j++){
switch(MAP[i][j]){
case 0:
g.setColor(Color.WHITE);
g.fillRect(j*CS, i*CS, CS, CS);
break;
case 1:
g.setColor(Color.BLACK);
g.fillRect(j*CS, i*CS, CS, CS);
break;
}
}
}
}
}
package com.laotou99.astar;
import java.awt.Point;
import java.io.Serializable;
import java.util.LinkedList;
import java.util.List;
//A*寻径处理用类(此类为演示用,并不意味着算法是最佳实现)
public class PathFinder {
// 路径优先等级list(此示例中为内部方法)
private LevelList _leveList;
// 已完成路径的list
private LinkedList _closedList;
// 地图描述
private int[][] _map;
// 行走区域限制
private int[] _limit;
// 以注入地图的2维数组及限制点描述初始化此类
public PathFinder(int[][] map, int[] limit) {
_map = map;
_limit = limit;
_leveList = new LevelList();
_closedList = new LinkedList();
}
// A*方式寻径,注入开始坐标及目标坐标后运算,返回可行路径的List
public List searchPath(Point startPos,Point objectPos){
// 初始化起始节点与目标节点
Node startNode = new Node(startPos);
Node objectNode = new Node(objectPos);
// 设定起始节点参数
startNode._costFromStart = 0;
startNode._costToObject=startNode.getCost(objectNode);
startNode._parentNode=null;
// 加入运算等级序列
_leveList.add(startNode);
// 当运算等级序列中存在数据时,循环处理寻径,直到levelList为空
while(!_leveList.isEmpty()){
// 取出并删除最初的元素
Node firstNode = (Node)_leveList.removeFirst();
// 判定是否和目标node坐标相等
if(firstNode.equals(objectNode)){
// 是的话即可构建出整个行走路线图,运算完毕
return makePath(firstNode);
}
else
{
// 否则 加入已验证List
_closedList.add(firstNode);
// 获得firstNode的移动区域
LinkedList _limit = firstNode.getLimit();
// 遍历
for(int i=0;i<_limit.size();i++){
//获得相邻节点
Node neighborNode = (Node)_limit.get(i);
//获得是否满足等级条件
boolean isOpen = _leveList.contains(neighborNode);
//获得是否已行走
boolean isClosed = _closedList.contains(neighborNode);
//判断是否无法通行
boolean isHit = isHit(neighborNode._pos.x,neighborNode._pos.y);
//当三则判定皆非时
if(!isOpen && !isClosed && !isHit){
//设定costFromStart
neighborNode._costFromStart = firstNode._costFromStart+1;
//设定costToObject
neighborNode._costToObject = neighborNode.getCost(objectNode);
//改变neighborNode父节点
neighborNode._parentNode=firstNode;
//加入level
_leveList.add(neighborNode);
}
}
}
}
//清空数据
_leveList.clear();
_closedList.clear();
//当while无法运行时,将返回null
return null;
}
//* 判定是否为可通行区域 *
private boolean isHit(int x,int y){
for(int i=0;i<_limit.length;i++){
if(_map[y][x]==_limit[i]){
return true;
}
}
return false;
}
//通过Node制造行走路径
private LinkedList makePath(Node node){
LinkedList path = new LinkedList();
// 当上级节点存在时
while(node._parentNode!=null){
// 在第一个元素处添加
path.addFirst(node);
// 将node赋值为parent node
node = node._parentNode;
}
// 在第一个元素处添加
path.addFirst(node);
return path;
}
private class LevelList extends LinkedList implements Serializable{
private static final long serialVersionUID = 1L;
//增加一个node
public void add(Node node){
for(int i=0;i<size();i++){
if(node.compareTo(get(i))<=0){
add(i,node);
return;
}
}
addLast(node);
}
}
}
package com.laotou99.astar;
import java.awt.Point;
import java.util.LinkedList;
//描述路径节点用类
public class Node implements Comparable {
public Point _pos; // 坐标
public int _costFromStart; // 开始地点数值
public int _costToObject; // 目标地点数值
public Node _parentNode; // 父节点
private Node() { }
// 以注入坐标点方式初始化Node
public Node(Point _pos){
this._pos=_pos;
}
// 返回路径成本
public int getCost(Node node){
// 获得坐标点间差值 公式:(x1, y1)-(x2, y2)
int m = node._pos.x - _pos.x;
int n = node._pos.y - _pos.y;
// 取两节点间欧几理德距离(直线距离)做为估价值,用以获得成本
return (int)Math.sqrt(m*m+n*n);
}
//检查node对象是否和验证对象一致
@Override
public boolean equals(Object node) {
if(_pos.x==((Node)node)._pos.x &&
_pos.y==((Node)node)._pos.y)
{
return true;
}
return false;
}
//--比较两点以获得最小成本对象
@Override
public int compareTo(Object node) {
int a1 = _costFromStart + _costToObject;
int a2 = ((Node)node)._costFromStart+((Node)node)._costToObject;
if(a1<a2)
{
return -1;
}
else if(a1==a2)
{
return 0;
}
else
{
return 1;
}
}
//--获得上下左右各点间移动限制区域
public LinkedList getLimit(){
LinkedList limit = new LinkedList<>();
int x = _pos.x;
int y = _pos.y;
//上下左右各点间移动区域(对于斜视地图,可以开启注释的偏移部分,此时将评估8个方位)
//上
limit.add(new Node(new Point(x,y-1)));
//右上
//limit.add(new Node(new Point(x+1,y-1)));
//右
limit.add(new Node(new Point(x+1,y)));
//右下
//limit.add(new Node(new Point(x+1,y+1)));
//下
limit.add(new Node(new Point(x, y+1)));
//左下
//limit.add(new Node(new Point(x-1,y+1)));
//左
limit.add(new Node(new Point(x-1,y)));
//左上
//limit.add(new Node(new Point(x-1, y-1)));
return limit;
}
}