A*寻路算法的lua实现
游戏中有敌我双方,有四十个方格,当轮到我方武将行动的时候,要先显示出我方武将可以行动的方位,这个就涉及到我方武将的行动力的大小来决定,预先做出路径的预算。这里还要考虑敌方以及地标(例如:×××、势头)的阻挡,以及特殊方格对武将行动力的消耗以及敌方的间隔阻挡规则。
当碰到这个问题的时候,问老大选择用什么寻路算法,他推荐的是Dijstra算法,但我看了之后感觉还不是很适合我的需求,第一:我觉得Dijstra算法是有向图的最佳路径选择,节点之间路径长度必须先知晓,但我这里四十个方格如果要两两制定感觉稍微复杂,而且随着武将的移动,地图还是时时变化的,感觉更复杂。第二:Distra算法没有阻挡考虑,当然也可以记录若干路径然后考虑阻挡选择最优路径,我感觉也稍复杂。
然而我的第一反应该需求A*算法挺接近的,然后咨询老大,老大说A*方格之间距离必须要均等不然比较复杂。然后再咨询公司其他稍有经验同事,当然各有个的说法,什么深度、广度遍历,感觉没一个确定的说法。让我选择就纠结了一天,不敢轻易选择,如果稍有考虑不慎,说不定就要做几天白用工,但最后我还是坚持自己的想法用A*来实现。
二、关于A*A*通用的计算公式F=G+H
G:表示从起点A移动到网格上指定方格的移动耗费(我这里没考虑斜方向移动),也可理解为节点的权重
H:表示从制定方格移动到终点B的预计耗费,这里一般就是计算距离*系数
三、寻路思想1.从起点A开始,把它添加到"开启列表"
2.寻找A点可到到的周围四个点,这里可到达是指没有阻挡并且已经不再关闭列表中的节点,并把他们添加进开启列表,并设置他们的父节点为A
3.从开启列表中删除A点并添加进关闭列表中,关闭列表就是存放的不需要检测的方格节点
4.检查它所有相邻并且合一到达的方格,如果这些方格还不再开启列表里的话就把他们加入开启列表,计算这些方格的GHF值并设置它的父节点
5.如果某个相邻方格 D 已经在 "开启列表" 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 "父方格" 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做
6.当发现开启列表中有终点目标方格的时候则说明路径已经找到。
关于详细的图解可以参考其他网友的详解,我这里就不详细写了。
四、找回路径我们找到最后一个点的时候然后层层往之前找到他的父节点迭代到最后不为空结束
五、数据结构Point类
[plain]view plaincopyprint?
module('Point',package.seeall)
--require("script/battle/BattleCommon")
--计算F值
functionCalcF(point)
point.F=point.G+point.H
end
functioncreate(posId)
localpoint={}
point.parentPoint={}
point.step=1--用于计算h值
localx,y=BattleCommon.convertPosIdToCoord(posId)
point.F=0
point.G=0
point.H=0
point.X=y--point.X范围[1,5]
point.Y=x--point.Y范围[1,8]
point.posId=posId
point.CalcF=CalcF
returnpoint
end
returnPoint
地形(Maze)结构
[plain]view plaincopyprint?
--根据一个table创建一个地形
functioncreate(tb)
localmaze={}
maze.step=1--格子与格子的基本距离,用于计算H值
maze.mazeArray=tb
maze.openList=TabledArray.create()--开启列表
maze.closeList=TabledArray.create()--关闭列表
maze.findPath=findPath
returnmaze
end
[plain]view plaincopyprint?
module('Maze',package.seeall)
require("script/battle/TabledArray")
require("script/battle/BattleCommon")
require("script/battle/Point")
----获取列表中F值最小的点
functiongetMinPoint(pointsList)
localminPoint=pointsList.tbl[1]
fori=1,pointsList:getSize()do
ifminPoint.F>pointsList.tbl[i].Fthen
minPoint=pointsList.tbl[i]
end
end
returnminPoint
end
----检测是否有阻挡,没有阻挡为true
functioncheckBlock(maze,x,y,roleFlag)--x范围[1,5]y范围[1,8]
ifroleFlag==BattleCommon.BATTLE_GROUP_ALLIESthen--我方阵营
ifmaze.mazeArray[x][y][1]==0ormaze.mazeArray[x][y][1]==1then
returntrue--没有阻挡
else
returnfalse
end
elseifroleFlag==BattleCommon.BATTLE_GROUP_ENEMYthen
ifmaze.mazeArray[x][y][1]==0ormaze.mazeArray[x][y][1]==2then
returntrue--没有阻挡
else
returnfalse
end
end
end
----列表中是否包含x,y的点
functionexistsXY(list,x,y)
iflist:getSize()>0then
fori,pointinpairs(list.tbl)do
ifpoint.X==xandpoint.Y==ythen
returntrue
end
end
end
returnfalse
end
--列表中是否包含某个点
functionexistsPoint(list,point)
fori,pinpairs(list.tbl)do
if(p.X==point.X)and(p.Y==point.Y)then
returntrue
end
end
returnfalse
end
----检测能达到的点
functioncanReach(maze,startPoint,x,y,roleFlag)
if(notcheckBlock(maze,x,y,roleFlag))orexistsXY(maze.closeList,x,y)then--关闭列表中包含这个点或者有阻挡
returnfalse
else
if(math.abs(x-startPoint.X)+math.abs(y-startPoint.Y)==1)then
returntrue
end
end
end
----获取相邻的点
functiongetSurroundPoints(maze,point,roleFlag)
localsurroundPoints=TabledArray.create()
fori=point.X-1,point.X+1do
forj=point.Y-1,point.Y+1do
ifi>0andi<6andj>0andj<9then--排除超过表姐
ifBattleCommon.distanceFromTo(point.posId,BattleCommon.convertToPositionId(j-1,i-1))<2then
ifcanReach(maze,point,i,j,roleFlag)then
surroundPoints:append(maze.mazeArray[i][j][2])
end
end
end
end
end
returnsurroundPoints--返回point点的集合
end
----计算G值
functionCalcG(point)
localG=point.G
localparentG=0
ifpoint.parentPointthen
parentG=point.parentPoint.G
end
returnG+parentG
end
functionfoundPoint(tempStart,point)
localG=CalcG(point)
ifG<point.Gthen
point.parentPoint=tempStart
point.G=G
point:CalcF()
end
end
functionnotFoundPoint(maze,tempStart,point)
point.parentPoint=tempStart
point.G=CalcG(point)
point:CalcF()
maze.openList:append(point)
end
functiongetPoint(list,data)
fori,pointinpairs(list.tbl)do
ifpoint.posId==data.posIdthen
returnpoint
end
end
returnnil
end
----寻找路径(起始路径)
localfunctionfindPath(maze,startPoint,endPoint,roleFlag)
maze.openList:append(startPoint)
whilemaze.openList:getSize()~=0do
--找出F的最小值
localtempStart=getMinPoint(maze.openList)
maze.openList:removeById(1)
maze.closeList:append(tempStart)
--找出它相邻的点
localsurroundPoints=getSurroundPoints(maze,tempStart,roleFlag)
fori,pointinpairs(surroundPoints.tbl)do
ifexistsPoint(maze.openList,point)then
--计算G值,如果比原来大,就什么都不做,否则设置他的父节点为当前节点,并更新G和F
foundPoint(tempStart,point)
else
--如果他们不再开始列表里面,就加入,并设置父节点,并计算GHF
notFoundPoint(maze,tempStart,point)
end
end
--如果最后一个存在则返回
ifgetPoint(maze.openList,endPoint)then
returngetPoint(maze.openList,endPoint)
end
end
returngetPoint(maze.openList,endPoint)
end
--根据一个table创建一个地形
functioncreate(tb)
localmaze={}
maze.step=1--格子与格子的基本距离,用于计算H值
maze.mazeArray=tb
maze.openList=TabledArray.create()--开启列表
maze.closeList=TabledArray.create()--关闭列表
maze.findPath=findPath
returnmaze
end
returnMaze
调用方法
[plain]view plaincopyprint?
functionprintPath(presentPoint)
localpathArray=TabledArray.create()
whilepresentPointdo
pathArray:preppend(presentPoint.posId)
presentPoint=presentPoint.parentPoint
end
localstartPoint=pathArray:get(2)
localendPoint=pathArray:getLast()
cclog(startPoint)
cclog(endPoint)
cclog("从"..startPoint.."到"..endPoint.."的路径是:")
fori,pinpairs(pathArray.tbl)do
cclog(p)
end
end
[plain]view plaincopyprint?
localarray=battleBoard:createBoxPoints(cRole:getFlag(),40)
localmaze=Maze.create(array)
localstartPoint=Point.create(cRole:getPositionId())
localendPoint=Point.create(40)
localpresentPoint=maze:findPath(startPoint,endPoint,cRole:getFlag())
printPath(presentPoint)
七、运行效果
手机测试效果还可以,貌似在255*255规模的方格规模上采用A*还是不会有太大的效率影响。如果需要交流欢迎联系。
声明:本站所有文章资源内容,如无特殊说明或标注,均为采集网络资源。如若本站内容侵犯了原著者的合法权益,可联系本站删除。