A *寻路算法
来源:互联网 发布:荒野猎人 知乎 编辑:程序博客网 时间:2024/05/16 04:53
直接上代码哦。是用as3实现的。注释很详细,直接看注释就OK。
一共两个as文件,一个是Maps.as,还有一个ARoad.as
Maps.as
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.events.TimerEvent;
import flash.text.TextField;
import flash.ui.Keyboard;
import flash.utils.Timer;
/**
* Author 吴日辉
*/
[SWF(width="900", height="540", frameRate="25", backgroundColor="0xffffff")]
public class Maps extends Sprite {
private var w:uint;//横向节点数
private var h:uint;//纵向节点数
private var wh:uint;//节点的宽与高
private var goo:Number;//控制地图中节点是否可通过的比率,数值在0.1-0.5之间,数值越小,地图上障碍越多
private var map:Sprite;//地图容器
private var mapArr:Array=new Array;//地图信息数组
private var roadMen:MovieClip;//寻路人
private var roadList:Array;//寻路返回的路径
private var roadTimer:Timer;//计数器
private var timer_i:uint=0;//配合计数器实现寻路人动画(很郁闷,不知如何在Timer事件中传递参数,只能这么解决)
private var roadinf:TextField = new TextField();
private var roadLen:TextField = new TextField();;
public function Maps() {
init();
}
//初始化---------------------》
private function init():void {
w=100;//地横向节点
h=60;//地图竖向节点
wh=9;//节点大小
goo=0.3;//地图障碍几率
createMaps();//生成随机地图
roadMens();//生成寻路人
roadTimer=new Timer(80,0);//定义计时器以完成寻路人行走动画
stage.addEventListener(KeyboardEvent.KEY_DOWN,keyDowns);//空格按键事件
}//End fun init
//当用户点击地图时开始寻路-------------》
private function mapMousedown(evt:MouseEvent):void {
var endX:Number=Math.floor((mouseX-map.x)/ wh);//将鼠标点击位置转化为节点索引值
var endY:Number=Math.floor((mouseY-map.y)/ wh);//将鼠标点击位置转化为节点索引值
var endPoint:MovieClip=mapArr[endY][endX];//从地图中取出鼠标点击的节点作为寻路终点
//如果目的地是可通过的则开始寻路
if (endPoint.go == 0) {
//每次寻路开始前将上次的路径清空
if (roadList) {
for each (var mc in roadList) {
mc.alpha=1;
}
roadList=[];
}//End if
roadTimer.stop();//停止走路
//动态取得寻路人当前位置的索引,并更新
roadMen.px=Math.floor(roadMen.x/wh);
roadMen.py=Math.floor(roadMen.y/wh);
var _ARoad:ARoad=new ARoad();//生成寻路实例
var oldTimes:int=new Date().getTime();//记录发送寻路方法时间
roadList=_ARoad.searchRoad(roadMen,endPoint,mapArr);//调用寻路方法(寻路人,目的地,地图信息)
var times:int=new Date().getTime()-oldTimes;//寻路方法执行完毕计算寻路花费时间
if (roadList.length>0) {
roadinf.htmlText="<FONT color='#00ff00'>本次寻路: "+times.toString()+"毫秒</FONT> ";
addChild(roadinf);
roadLen.htmlText="<FONT color='#00ff00'>路径长度: "+roadList.length.toString()+"</FONT>";//路径长度
roadLen.y = 20;
addChild(roadLen);
MC_play(roadList);//让寻路人行走
} else {
roadinf.htmlText="<FONT color='#00ff00'>对不起,无路可走</FONT>";
addChild(roadinf);
}//End if
}//End if
}//End fun
//寻路人行走----------------------》
private function MC_play(roadList:Array):void {
roadList.reverse();//倒转数组
roadTimer.stop();
timer_i=0;
roadTimer.addEventListener(TimerEvent.TIMER,goMap);
roadTimer.start();
for each (var mc in roadList) {
mc.alpha=0.3;
}//End if
}//End fun
//每隔一定时间行走一格----------------》
private function goMap(evt:TimerEvent):void {
var tmpMC:MovieClip=roadList[timer_i];
roadMen.x=tmpMC.x;
roadMen.y=tmpMC.y;
tmpMC.alpha=1;//经过路径后消除其标识状态
timer_i++;
//达到终点行走停止
if (timer_i>=roadList.length) {
roadTimer.stop();
}//End if
}//End fun
//生成地图并存储信息-----------------》
private function createMaps() {
map=new Sprite ;//地图容器
addChild(map);
map.addEventListener(MouseEvent.MOUSE_DOWN,mapMousedown);//鼠标点击地图事件
for (var y:uint=0; y < h; y++) {
mapArr.push(new Array );//建立二维数组存储地图信息
for (var x:uint=0; x < w; x++) {
var mapPoint:uint=Math.round(Math.random() - goo);//随机节点可通过与不可通过
var point:MovieClip=drawRect(mapPoint);//画出节点
mapArr[y].push(point);//将节点加入地图数组中
mapArr[y][x].px=x;//当前节点横向索引位置
mapArr[y][x].py=y;//当前节点纵向索引位置
mapArr[y][x].go=mapPoint;//当前节点是否可通过
mapArr[y][x].x=x * wh;//当前节点的x位置
mapArr[y][x].y=y * wh;//当前节点的y位置
map.addChild(mapArr[y][x]);//将节点显示到地图容器中
}//End for x
}//End for y
}//End fun
//空格键重生地图--------------------》
private function keyDowns(evt:KeyboardEvent) {
var _key=evt.keyCode;
if ( _key == Keyboard.SPACE) {
removeChild(map);
mapArr=[];
createMaps();
roadMens();//生成寻路人
roadTimer.stop();
}//End if
}//End if
//根据传入的随机数画出不同的节点(即可通过/不可通过/寻路人)---》
private function drawRect(mapPoint:uint):MovieClip {
var _tmp:MovieClip=new MovieClip;
var color:uint;
switch (mapPoint) {
case 0 :
color=0x999999;//可通过为灰色
break;
case 1 :
color=0x000000;//不可通过为黑色
break;
default :
color=0xFF0000;//否则为寻路人
}//End switch
_tmp.graphics.beginFill(color);
_tmp.graphics.lineStyle(0.2,0xFFFFFF);
_tmp.graphics.drawRect(0,0,wh,wh);
_tmp.graphics.endFill();
return _tmp;
}//End fun drawRect
//生成寻路人-------------------------》
private function roadMens() {
roadMen=drawRect(2);
//让寻路人随机出现在地图上并设置寻路人的横纵向索引位置----->
var _tmpx:uint=Math.round(Math.random() * (w-1));
var _tmpy:uint=Math.round(Math.random() * (h-1));
roadMen.px=_tmpx;//记录所在位置索引值
roadMen.py=_tmpy;
roadMen.x=_tmpx * wh;
roadMen.y=_tmpy * wh;
mapArr[_tmpy][_tmpx].go=0;//让寻路人出现的地图点变为可通过
map.addChild(roadMen);
}
}
}
ARoad.as
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.utils.Timer;
import flash.events.TimerEvent;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.text.TextField;
/**
* Author 吴日辉
*/
public class ARoad extends Sprite {
private var startPoint:MovieClip;//寻路起点
private var endPoint:MovieClip;//要到达的目的地
private var mapArr:Array;//地图信息
private var w:uint;//地图的横向节点数
private var h:uint;//地图的纵向节点数
private var openList:Array=new Array();//开启列表
private var closeList:Array=new Array();//关闭列表
private var roadArr:Array=new Array();//返回的路径
public function ARoad() {
}//End Fun
//对外的寻路接口
public function searchRoad(start:MovieClip,end:MovieClip,map:Array) {
startPoint=start;//获得寻路起点
endPoint=end;//获得要到达的目的地
mapArr=map;//获得地图信息
w=mapArr[0].length-1;//获得地图横向的节点数
h=mapArr.length-1;//获得地图纵向的节点数
openList.push(startPoint);//将起点加入开启列表
while (true) {
if (openList.length<1) {//无路可走
//trace("无路可走");
return roadArr;
break;
}
var thisPoint:MovieClip=openList.splice(getMinF(),1)[0];//每次取出开启列表中F最低的节点
if (thisPoint==endPoint) {//找到路径
//trace("找到路径");
//从终点开始往回找父节点,以生成路径列表,直到父节点为起始点
while (thisPoint.father!=startPoint.father) {
roadArr.push(thisPoint);
thisPoint=thisPoint.father;
}
return roadArr;//返回路径列表
break;
}
closeList.push(thisPoint);//把当前节点加入关闭列表
addAroundPoint(thisPoint);//开始检查当前节点四周的节点
}//End while
}//End Fun
//检查当前节点四周的八个节点,可通过并不在关闭及开启列表中的节点加入至开启列表
private function addAroundPoint(thisPoint:MovieClip) {
var thisPx:uint=thisPoint.px;//当前节点横向索引
var thisPy:uint=thisPoint.py;//当前节点纵向索引
//添加左右两个直点的同时过滤四个角点,以提高速度。
//即如果左边点不存在或不可通过则左上左下两角点就不需检查,右边点不存在或不可通过则右上右下两角点不需检查
//后面添加四个为角点,角点的判断为,自身可通过&&它相邻的两个当前点的直点都可通过
if (thisPx>0 && mapArr[thisPy][thisPx - 1].go==0 ) {//加入左边点
if (!inArr(mapArr[thisPy][thisPx - 1],closeList)) {//是否在关闭列表中
if (!inArr(mapArr[thisPy][thisPx - 1],openList)) {//是否在开启列表中
setGHF(mapArr[thisPy][thisPx - 1],thisPoint,10);//计算GHF值
openList.push(mapArr[thisPy][thisPx - 1]);//加入节点
} else {
checkG(mapArr[thisPy][thisPx-1],thisPoint);//检查G值
}//End if
}//End if
//加入左上点
if (thisPy>0 && mapArr[thisPy-1][thisPx - 1].go==0 && mapArr[thisPy - 1][thisPx].go==0) {
if (!inArr(mapArr[thisPy-1][thisPx - 1],closeList) && !inArr(mapArr[thisPy-1][thisPx - 1],openList)) {
setGHF(mapArr[thisPy - 1][thisPx-1],thisPoint,14);//计算GHF值
openList.push(mapArr[thisPy-1][thisPx - 1]);//加入节点
}//End if
}//End if
//加入左下点
if (thisPy<h && mapArr[thisPy+1][thisPx - 1].go==0 && mapArr[thisPy + 1][thisPx].go==0) {
if (!inArr(mapArr[thisPy+1][thisPx - 1],closeList) && !inArr(mapArr[thisPy+1][thisPx - 1],openList)) {
setGHF(mapArr[thisPy + 1][thisPx-1],thisPoint,14);//计算GHF值
openList.push(mapArr[thisPy+1][thisPx - 1]);//加入节点
}//End if
}//End if
}//End if
if (thisPx<w && mapArr[thisPy][thisPx + 1].go==0) {//加入右边点
if (!inArr(mapArr[thisPy][thisPx + 1],closeList)) {//是否在关闭列表中
if (!inArr(mapArr[thisPy][thisPx + 1],openList)) {//是否在开启列表中
setGHF(mapArr[thisPy][thisPx + 1],thisPoint,10);//计算GHF值
openList.push(mapArr[thisPy][thisPx + 1]);//加入节点
} else {
checkG(mapArr[thisPy][thisPx + 1],thisPoint);//检查G值
}//End if
}//End if
//加入右上点
if (thisPy>0 && mapArr[thisPy-1][thisPx +1].go==0 && mapArr[thisPy - 1][thisPx].go==0) {
if (!inArr(mapArr[thisPy-1][thisPx + 1],closeList) && !inArr(mapArr[thisPy-1][thisPx + 1],openList)) {
setGHF(mapArr[thisPy - 1][thisPx+1],thisPoint,14);//计算GHF值
openList.push(mapArr[thisPy-1][thisPx + 1]);//加入节点
}//End if
}//End if
//加入右下点
if (thisPy<h && mapArr[thisPy+1][thisPx + 1].go==0 && mapArr[thisPy + 1][thisPx].go==0) {
if (!inArr(mapArr[thisPy+1][thisPx+ 1],closeList) && !inArr(mapArr[thisPy+1][thisPx + 1],openList)) {
setGHF(mapArr[thisPy + 1][thisPx+1],thisPoint,14);//计算GHF值
openList.push(mapArr[thisPy+1][thisPx + 1]);//加入节点
}//End if
}//End if
}//End if
if (thisPy>0 && mapArr[thisPy - 1][thisPx].go==0) {//加入上面点
if (!inArr(mapArr[thisPy - 1][thisPx],closeList)) {//是否在关闭列表中
if (!inArr(mapArr[thisPy - 1][thisPx],openList)) {//是否在开启列表中
setGHF(mapArr[thisPy - 1][thisPx],thisPoint,10);//计算GHF值
openList.push(mapArr[thisPy - 1][thisPx]);//加入节点
} else {
checkG(mapArr[thisPy - 1][thisPx],thisPoint);//检查G值
}//End if
}//End if
}//End if
if (thisPy<h && mapArr[thisPy + 1][thisPx].go==0) {//加入下面点
if (!inArr(mapArr[thisPy + 1][thisPx],closeList)) {//是否在关闭列表中
if (!inArr(mapArr[thisPy + 1][thisPx],openList)) {//是否在开启列表中
setGHF(mapArr[thisPy + 1][thisPx],thisPoint,10);//计算GHF值
openList.push(mapArr[thisPy + 1][thisPx]);//加入节点
} else {
checkG(mapArr[thisPy + 1][thisPx],thisPoint);//检查G值
}//End if
}//End if
}//End if
}//End Fun
//判断当前点是否在某个列表中----------------------------》
private function inArr(obj:MovieClip,arr:Array):Boolean {
for each (var mc in arr) {
if (obj == mc) {
return true;
}//End if
}//End for
return false;
}//End Fun
//设置节点的G/H/F值----------------------------》
private function setGHF(point:MovieClip,thisPoint:MovieClip,G) {
if (!thisPoint.G) {
thisPoint.G=0;
}
point.G=thisPoint.G+G;
//H值为当前节点的横纵向到终点的节点数×10
point.H=(Math.abs(point.px - endPoint.px) + Math.abs(point.py - endPoint.py))*10;
point.F=point.H + point.G;//计算F值
point.father=thisPoint;//指定父节点
}//End Fun
//检查新的G值以判断新的路径是否更优
private function checkG(chkPoint:MovieClip,thisPoint:MovieClip) {
var newG=thisPoint.G + 10;//新G值为当前节点的G值加上10(因为只检查当前节点的直点)
if (newG <= chkPoint.G) {//如果新的G值比原来的G值低或相等,说明新的路径会更好
chkPoint.G=newG;//更新G值
chkPoint.F=chkPoint.H+newG;//同时F值重新被计算
chkPoint.father=thisPoint;//将其父节点更新为当前点
}//End if
}//End Fun
//获取开启列表中的F值最小的节点,返回的是该节点所在的索引
private function getMinF():uint {
var tmpF:uint=100000000;//用以存放最小F值(这里先假定了一个很大的数值)
var id:uint=0;
var rid:uint;
for each (var mc in openList) {
//如果列表中的当前节点的F值比目前存放的F值小,就将F值更新为当前节点的F值,否则就什么都不做
//这样循环和列表中所有节点的F值比较完成后,最后用以存放最小F值里的F值就是最小的
if (mc.F<tmpF) {
tmpF=mc.F;
rid=id;//同时更新返加的索引值为当前节点的索引
}
id++;//因为for each方法是从数组中的第一个对象开始遍历,而每比一次id+1刚好可以匹配其索引位置
//也可以使用FOR遍历,但FLASH中用 FOR EACH方法效率更高
}//End for
return rid;//比较完成后返回最小F值所在的索引
}//End fun
}//End Class
}//End package
总结:对openList的维护开销较大(因为每次都需要遍历得出F值最小的那个节点,可以采取二叉树排序来解决)。上述算法适合小地图。
最后推荐一篇文章。我觉讲的非常详细。http://www.cppblog.com/christanxw/archive/2006/04/07/5126.html
- A*寻路算法
- A* 寻路算法
- A* 寻路算法
- A*寻路算法
- A*寻路算法
- A*寻路算法
- A*寻路算法
- A* 寻路算法
- A*寻路算法
- A* 寻路算法
- A* 寻路算法
- A* 寻路算法
- A* 寻路算法
- A*寻路算法
- A* 寻路算法
- A* 寻路算法
- A *寻路算法
- A* 寻路算法
- wamp server配置服务器
- 寻找两个链表的第一个交点-微策略
- 简单的深度优先搜索HDU1045
- win7之GetPrivateProfileStringW和WritePrivateProfileStringW访问C:\\ProgramFiles\\目录下文件权限不足问题
- 函数调用入栈基本步骤(感觉和进程的栈帧结构一块看会比较容易理解)
- A *寻路算法
- JSP自定义标签(三) 多选控件(3)
- JAX-RS
- 科鲁兹掀背车1.6T 流动的魅力
- 计算机网络笔记(第一章)
- 查看python已经安装的模块
- 如何利用block回调--神级理解
- 【android】设置View字体点击变色和边框背景设置
- presentModalViewController的使用