FLEXAS3做FlashGame地图寻路实例.docx
《FLEXAS3做FlashGame地图寻路实例.docx》由会员分享,可在线阅读,更多相关《FLEXAS3做FlashGame地图寻路实例.docx(22页珍藏版)》请在冰豆网上搜索。
FLEXAS3做FlashGame地图寻路实例
FLEXAS3做FlashGame地图寻路实例
1.总结目的
在地图中,通过鼠标或者由程序自动运行,让一个人物自动从地图的一点走到另一点。
需要计算两点之间的最优路线,要实现这样的寻路算法。
最常规和最简单的方法,使用A*算法。
本篇总结不具体讲解A*算法的实现以及原理,主要告诉怎么用。
后面的总结中我会陆续把A*算法进行详细分析,希望大家学习。
2.开发流程
2.1.地图加载
1)在寻路过程中,地图是被分为若干个小格子,当然,根据格子数量的不同,路线长度的不同,计算寻路的时间也就不同。
上面是有一个由20*20的地图拼起来的地图,每一个地图块是有20像素*20像素元件做成。
每一个地图块是2帧构成,地图帧是人物可以行走的路线,第二帧是障碍物(桌子)。
上面的地图是路线和障碍物8比2的概率生成,参考代码:
Block是地图块元件类。
用Flashcs3已经做好。
this.mass_x_count是指地图X轴上板块数量
this.mass_y_count是指地图Y轴上板块数量
distancex是指地图X轴离边界Left的距离
distancey是指地图Y轴离边界Top的距离
aNodeMap是一个2维数组,记录着每一个地图块当前的状态,是障碍物还是路线。
地图初始化中,根据两个For循环嵌套,生成一个表格状的地图。
这里我不在详细介绍,详细会AS3的一定可以看的懂。
2.2.地图上移动人物加载
1)路线寻路肯定要有一个元件在地图上移动,才可以正确的显示出寻路的过程。
无论是一个人物移动或多个人物移动,基本做法相同,下面我们就来加载一个人物物件到地图上来。
Waiter是一个移动的任务元件。
在这里我们叫“服务员元件”
Timer是为服务员增加一个timer事件,主要目的是为了服务员从一个地图块移动到另一个地图块,不是跳过去的,而是慢慢的移动过去的。
此方法主要在加载完地图后调用,指定人物加载的X,Y坐标,进行加载。
2.3.选择移动目的地
1)地图和服务员加载完成后,我们需要选择服务员移动的目的地,选择的方式有很多。
举个例子,人人餐厅中客人进来就餐,首先进门后,程序会自动寻找有空位的X,Y坐标,然后计算路线,客人就会沿着路线走进去。
同样服务员也会根据客人进来餐厅的循序,以及客人所做的座椅的坐标进行计算路线,然后走过去服务。
2)由于我们只是一个例子,所以就有我们鼠标确定目的地。
this.addEventListener(MouseEvent.CLICK,clickWay);
这个事件通过,用户鼠标点击地图上路线进行调用。
2.4.路线寻找,人物移动
1)在地图增加完后我们增加一个A*算法实例
AStar是A*算法的一个封装类。
我们需要把我们地图上路线和障碍物二维数组传递给算法。
这个是在地图加载后完成
2)确定移动目的地后计算路线
上面代码是在选择目的地后补充的代码
设置A*(asrar)算法的其实点和目的地。
起始点和目的地封装成ANode对象给算法的。
ANode是一个算法计算中节点的保存,记录着节点的位置X,Y,节点ID,以及使用曼哈顿方法计算F=G+HF、G、H值的保存。
从而做为路线计算中开启列表进行存储。
最后会调用DoSearch判断路线的获取是否成功。
获取的路线调用astar.aPath方法进行获取。
启动timer进行服务员移动操作。
3)服务员移动
在移动服务之前,我们确定,我们寻找到路线,此服务员的上一个路线已经完成。
代码如下:
这个方法是在寻找到路线后,timer启动执行的。
bMove表示是否移动服务员。
player代表当前移动中的服务员对象,他是在创建服务员的时候保存在全局变量中。
A*算法会在下面参考代码中会有
3.参考代码
A*寻路算法代码:
importflash.geom.Point;
/*
*A*寻路算法
*@authorliujia
*@version1.0
*/
publicclassAStar{
//垂直或水平移动一格的代价
privateconstCOST_STRAIGHT:
int=10;
//斜向移动一格的代价
privateconstCOST_DIAGONAL:
int=14;
//起始节点ANode
publicvarndStart:
ANode;
//目标节点ANode
publicvarndEnd:
ANode;
//当前节点ANode
publicvarndCurrent:
ANode;
//最大寻路步数限制
privatevarnMaxTry:
int;
//尝试次数计数器
privatevarnCrtTry:
int;
//开放表,元素为ANode类型
privatevaraOpenList:
Array;
//关闭表,元素为ANode类型
privatevaraCloseList:
Array;
//八个方向数组,从节点正上方开始,顺时针排列
privateconstaDir:
Array=[[0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1]];
//地图节点数组
publicvaraNodeMap:
Array=[];
//地图大小
publicvarmapwidth:
int;
publicvarmapheight:
int;
//记录节点在aOpenList数组的位置
privatevarnum:
int;
//路径数组
publicvaraPath:
Array;
//是否找到路径
publicvarbPathFind:
Boolean;
//构造函数
publicfunctionAStar(amap:
Array){
nMaxTry=600;
nCrtTry=0;
Init();
mapwidth=amap[0].length;
mapheight=amap.length;
aNodeMap=amap;
}
//初始化
publicfunctionInit():
void{
bPathFind=false;
aOpenList=[];
aCloseList=[];
aPath=[];
}
//添加到open表
publicfunctionaddFirstOpen():
void{
aOpenList.push(ndStart);
}
//取得指定点周围可通过的点,从正上方开始
privatefunctionGetRound(apos:
Array):
Array{
vararr:
Array=newArray();
for(vari:
int=0;ivarxp=apos[0]+aDir[i][0];
varyp=apos[1]+aDir[i][1];
if(IsOutRange([xp,yp])||IsStart([xp,yp])||!
IsPass([xp,yp])||IsCorner([xp,yp])||IsInClose([xp,yp]))
continue
arr.push([xp,yp]);
}
returnarr;
}
//是否超出地图范围
privatefunctionIsOutRange(apos:
Array):
Boolean{
if(apos[0]<0||apos[0]>=mapwidth||apos[1]<0||apos[1]>=mapheight)
returntrue;
returnfalse;
}
//是否是起点
privatefunctionIsStart(apos:
Array):
Boolean{
if(apos[0]==ndStart.pos[0]&&apos[1]==ndStart.pos[1])
returntrue;
returnfalse;
}
//是否可以通过
privatefunctionIsPass(apos:
Array):
Boolean{
if(IsOutRange(apos)){
returnfalse;
}else{
return(aNodeMap[apos[1]][apos[0]]>0?
false:
true);
}
}
//是否是拐角
privatefunctionIsCorner(apos:
Array):
Boolean{
if(IsPass(apos)){
if(apos[0]>ndCurrent.pos[0]){
if(apos[1]>ndCurrent.pos[1]){
if(!
IsPass([apos[0]-1,apos[1]])||!
IsPass([apos[0],apos[1]-1]))
returntrue;
}
elseif(apos[1]if(!
IsPass([apos[0]-1,apos[1]])||!
IsPass([apos[0],apos[1]+1]))
returntrue;
}
}
elseif(apos[0]if(apos[1]>ndCurrent.pos[1]){
if(!
IsPass([apos[0]+1,apos[1]])||!
IsPass([apos[0],apos[1]-1]))
returntrue;
}
elseif(apos[1]if(!
IsPass([apos[0]+1,apos[1]])||!
IsPass([apos[0],apos[1]+1]))
returntrue;
}
}
}
returnfalse;
}
//是否在开启列表中
//获得传入参数在aOpenlist数组的位置,如不存在返回false,存在为true,位置索引保存到变量num中。
privatefunctionIsInOpen(apos:
Array):
Boolean{
varbool:
Boolean=false;
varid=apos[1]*mapwidth+apos[0];
for(vari=0;iif(aOpenList[i].id==id){
bool=true;
num=i;
break;
}
}
returnbool;
}
//是否在关闭列表中
privatefunctionIsInClose(apos:
Array):
Boolean{
varbool:
Boolean=false;
varid=apos[1]*mapwidth+apos[0];
for(vari=0;iif(aCloseList[i].id==id){
bool=true;
break;
}
}
returnbool;
}
//取得F值,参数为某一节点周围的节点
privatefunctionGetF(around:
Array):
void{
//F,综合的距离值;
//H,给定节点到目标点的距离值;
//G,起点到给定节点的距离值
varF:
int,H:
int,G:
int;
varapos:
Array;
for(vari:
int=0;iapos=around[i];
//是否与起点在同一直线上
if(apos[0]==ndStart.pos[0]||apos[1]==ndStart.pos[1]){
G=ndCurrent.G+COST_STRAIGHT;
}else{
G=ndCurrent.G+COST_DIAGONAL;
}
//如果当前点已存在aOpenlist数组中
if(IsInOpen(apos)){
varopos:
ANode=aOpenList[num]asANode;
//如果当前点G值更小,更改父节点
if(Gopos.F=G+opos.H;
opos.G=G;
opos.pid=ndCurrent.id;
}else{
G=opos.G;
}
}
//否则将当前点添加到aOpenList数组
else{
H=(Math.abs(ndEnd.pos[0]-apos[0])+Math.abs(ndEnd.pos[1]-apos[1]))*COST_STRAIGHT;
F=G+H;
varnewnode:
ANode=newANode(apos,apos[1]*mapwidth+apos[0],0,ndCurrent.id);
newnode.F=F;
newnode.G=G;
newnode.H=H;
aOpenList.push(newnode);
}
}
}
//搜索路径
publicfunctionDoSearch():
Boolean{
aOpenList=[];
aCloseList=[];
addFirstOpen();
while(aOpenList.length){
nCrtTry++;
//如果超出寻路步数限制
if(nCrtTry>nMaxTry){
destroyData();
returnfalse;
}
GetF(GetRound(ndCurrent.pos));
//按照F值由大到小的顺序排列开启列表
aOpenList.sortOn("F",Array.NUMERIC|Array.DESCENDING);
//将开启列表最后一位元素列入关闭列表
varlastNode:
ANode=aOpenList[aOpenList.length-1];
aCloseList.push(lastNode);
ndCurrent=lastNode;
if(aOpenList.length>1)
aOpenList.pop();
//如果当前节点是目标节点,路径找到,返回true
if(ndCurrent.id==ndEnd.id){
aPath=GetPath();
destroyData();
bPathFind=true;
ndStart=ndCurrent;
returntrue;
}
}
bPathFind=false;
destroyData();
aPath=[];
returnfalse;
}
//清空各数组
privatefunctiondestroyData():
void{
aOpenList=[];
aCloseList=[];
nCrtTry=0;
}
//取得路径数组
privatefunctionGetPath():
Array{
varapath:
Array=[];
vartmpnode:
ANode=aCloseList[aCloseList.length-1]asANode;
apath.push(tmpnode.pos);
varinc:
int=0;
while(inc<=aCloseList.length){
inc++
for(vari:
int=0;iif(aCloseList[i].id==tmpnode.pid){
tmpnode=aCloseList[i];
apath.push(tmpnode.pos)
}
if(tmpnode.id==ndStart.id)
break;
}
}
returnapath;
}
}
ANode节点对象:
publicclassANodeextendsObject{
publicvarpid:
int;
publicvarid:
int;
publicvarpos:
Array;
publicvarG:
int;
publicvarH:
int;
publicvarF:
int;
publicvarblock:
int;//1isblock,0ispass
publicfunctionANode(_pos:
Array,_id:
int,_block:
int=0,_pid:
int=0){
id=_id;
pid=_pid;
pos=_pos;
block=_block;
G=0;
H=0;
F=0;
}
}
寻路实例主类:
importflash.events.Event;
importflash.events.MouseEvent;
importflash.events.TimerEvent;
importflash.utils.Timer;
importmx.containers.Canvas;
importmx.controls.Alert;
publicclassSceneMainextendsCanvas
{
//地板离地图的边距
privatevardistancex:
int=30;
privatevardistancey:
int=30;
//地板的默认宽度和高度
privatevarfloorw:
int=20;
privatevarfloorh:
int=20;
//地板块数量
privatevarmass_x_count:
int;
privatevarmass_y_count:
int;
//服务员行走速度
privatevarinterval:
int=50;
//A*算法实例
privatevarastar:
AStar;
//移动属性
privatevarbMove:
Boolean;
//路径数组
privatevaraPath:
Array;
//路径数组指针
privatevarpathpoint:
int;
//地图数组
privatevaraNodeMap:
Array;
//服务员对象
privatevarplayer:
Waiter=null;
//服务员沿路线行走timer
privatevartimer:
Timer=newTimer(interval);
publicfunctionSceneMain(x:
int,y:
int)
{
aNodeMap=newArray();
this.mass_x_count=x;
this.mass_y_count=y;
//初始化地图
this.initScene();
//初始化移动服务员
this.loadMoveRen(distancey,distancex);
this.addEventListener(MouseEvent.CLICK,clickWay);
}
/**
*初始化地图
*/
publicfunctioninitScene():
void{
varblock:
Block=newBlock();
this.width=block.width*this.mass_x_count+distancey;
this.height=block.height*this.mass_y_count+distancex;
for(vari:
int=0;iaNodeMap[i]=newArray();
for(varj:
int=0;jvarblock1:
Block=newBlock();
if(i==0&&j==0){
block1.gotoAndStop
(1);
}else{
vargailu:
Number=Math.floor(Math.random()*10)+1;
block1.go