您的位置:首页 > 其它

A*寻路算法

2016-03-05 17:33 441 查看

实现原理

算法名称:A*算法

应用场景:游戏里的自动寻路

原理:

1,从起点开始找它周围可以走的格子,算出可走格子中F值最小的格子,再就以这个格子作为新的中心点,又同样找其周围可以走的格子,以此类推,直至找到目的点。

2,确定最优格子是取F值最小为依据,那么F值怎么算?F = G + H,G:表示从起点移动到指定格子的耗费(一般横竖着的格子耗费为10(or 1),斜着的格子耗费为14(or 1.4),等于其父节点G + 自己的G的和)。H:表示从指定格子移动到终点的预计耗费(一般就是指定点到终点相差的X,Y绝对值的和)。

3,每次找的周围格子都会放入到“开放列表”中,如果下次周围点某几个“开放列表”中已经存在,是否修改这些存在的点属性,是看新计算的G值,如果G值 > 原来的G值,则什么也不做,否则,就以新的G值替换,同时改变其父节点。

4,每个格子都以其中心点作为父节点,这样链接起来,就像一个链表结构,这样直到找到目的点后,就可以通过目的点的父节点这样一级级还原出路径了。

5,每个格子的属性:x坐标,y坐标,G值,H值,F值(F=G + H),父格子。(这种结构类似链表)

寻路步骤

1,从起点A开始, 把它作为待处理的方格存入一个”开启列表”, 开启列表就是一个等待检查方格的列表.

2,寻找起点A周围可以到达的方格, 将它们放入”开启列表”, 并设置它们的”父方格”为A.

3,从”开启列表”中删除起点 A, 并将起点 A 加入”关闭列表”, “关闭列表”中存放的都是不需要再次检查的方格.

4, 从 “开启列表” 中选择 F 值最低的方格C.

5,把它从 “开启列表” 中删除, 并放到 “关闭列表” 中.

6,检查它所有相邻并且可以到达 (障碍物和 “关闭列表” 的方格都不考虑) 的方格. 如果这些方格还不在 “开启列表” 里的话, 将它们加入 “开启列表”, 计算这些方格的 G, H 和 F 值各是多少, 并设置它们的 “父方格”为C .

7, 如果某个相邻方格 D 已经在 “开启列表” 里了, 检查如果用新的路径 (就是经过C 的路径) 到达它的话, G值是否会更低一些, 如果新的G值更低, 那就把它的 “父方格” 改为目前选中的方格 C, 然后重新计算它的 F 值和 G 值 (H 值不需要重新计算, 因为对于每个方块, H 值是不变的). 如果新的 G 值比较高, 就说明经过 C 再到达 D 不是一个明智的选择, 因为它需要更远的路, 这时我们什么也不做.

8,然后继续找F值最小的,如此循环下去…

下面是一张寻路的示例图:绿块是起点,蓝块是障碍,红块是终点,天蓝框是中心点,绿框是检测过的点,框左上角F值,框左下角是G值,框右下角是H值,红线是寻路过程。



最终通过父节点还原路径,如下图:(红点表示最终路径)



示例代码(Lua)

这个是项目中的源码,寻路核心就是用的上面讲的A*算法。

1,格子点结构定义(Node)

local Point                         = class("Point")

function Point:ctor(pos)
self.parentPoint                = nil
self.F                          = 0
self.G                          = 0
self.H                          = 0
self.x                          = pos.x
self.y                          = pos.y
end

--计算F值
function Point:CalcF( point )
self.F                          = self.G + self.H
end

return Point


2,A*寻路类

local FindPath                      = class("FindPath")

function FindPath:ctor()
--路径列表
self.pathArry                   = {}
end

--获取列表中F值最小的点
function FindPath:getMinPoint(pointList)
local minPoint                  = pointList[1]
for i = 1, table.getn(pointList) do
if minPoint.F > pointList[i].F then
minPoint                = pointList[i]
end
end
return minPoint
end

--从开启列表移除点
function FindPath:removePoint(point, pointList)
if table.getn(pointList) <= 0 then
return
end

for i=table.getn(pointList), 1, -1 do
if pointList[i].x == point.x and pointList[i].y == point.y then
table.remove(pointList, i)
end
end
end

--列表中是否包含点
function FindPath:existPoint(pointList, point)
for i, p in pairs(pointList) do
if (p.x == point.x) and (p.y == point.y) then
return i
end
end
return false
end

--获取相邻的点
function FindPath:getSurroundPoints(point)
local surroundPoints            = {}
for i=point.x - 1, point.x + 1 do

for j=point.y - 1, point.y + 1 do
if i == point.x and j == point.y then
--点自己
elseif not self:existPoint(self.closeList, cc.p(i, j)) then
if mapLayer:isObstacleByTile(cc.p(i, j)) then
--障碍
else
local tPos  = require("app.control.Point").new(cc.p(i, j))
local dPos  = self.endPos
if i == point.x or j == point.y then
tPos.G  = 1
else
tPos.G  = 1.4
end
tPos.H  = math.abs(dPos.x-tPos.x) + math.abs(dPos.y-tPos.y)
tPos.parentPoint    = point
table.insert(surroundPoints, tPos)
end
end
end

end

return surroundPoints   --返回point点的集合
end

--计算G值
function FindPath:CalcG(point)
local G                         = point.G
local parentG                   = 0
if point.parentPoint then
parentG                     = point.parentPoint.G
end
return G + parentG
end

function FindPath:foundPoint(tempStart, point, idx)
local lastP                     = self.openList[idx]
local G                         = self:CalcG(point)
if G < lastP.G then
point.parentPoint           = tempStart
point.G                     = G
point:CalcF()
self.openList[idx]          = point
end
end

function FindPath:notFoundPoint(tempStart, point)
point.parentPoint               = tempStart
point.G                         = self:CalcG(point)
point:CalcF()
table.insert(self.openList, point)
end

--获得角色真实位置(移动一格完成后的位置)
function FindPath:getRoleRealPos()
local curPos                    = nil
if self.roleTgtPos then
curPos                      = self.roleTgtPos
else
--角色位置
local x, y                  = myRole:getPosition()
curPos                      = cc.p(x, y)
end

return mapLayer:tileCoordForPos(curPos)
end

--开始寻找路径
function FindPath:startFindPath(endPos)
local startPos                  = self:getRoleRealPos()
if endPos.x == startPos.x and endPos.y == startPos.y then
printInfo("click same point")
return false
end
--print("startPos:", startPos.x, startPos.y)
--print("endPos:", endPos.x, endPos.y)

--初始化值
self.openList                   = {}
self.closeList                  = {}
self.pathArry                   = {}
local findPos                   = nil
local isFind                    = true
startPos                        = require("app.control.Point").new(startPos)
self.endPos                     = require("app.control.Point").new(endPos)

table.insert(self.openList, startPos)
--寻路逻辑
while table.getn(self.openList) > 0 do
--找出F的最小值
local tempStart             = self:getMinPoint(self.openList)
self:removePoint(tempStart, self.openList)
table.insert(self.closeList, tempStart)

--找出它相邻的点
local surroundPoints        = self:getSurroundPoints(tempStart)
for i,point in pairs(surroundPoints) do
local idx               = self:existPoint(self.openList, point)
if idx then
--计算G值,如果比原来大,就什么都不做,否则设置他的父节点为当前节点,并更新G和F
self:foundPoint(tempStart, point, idx)
else
--如果他们不再开始列表里面,就加入,并设置父节点,并计算GHF
self:notFoundPoint(tempStart, point)
end
end

--如果最后一个存在则返回
local idx                   = self:existPoint(self.openList, self.endPos)
if idx then
findPos                 = self.openList[idx]
break
end
end

--添加路径
--printInfo("find path")
while findPos do
--转换为地图坐标
--printInfo("%d, %d", findPos.x, findPos.y)
local tempPos               = mapLayer:posForTileGood(findPos)
--printInfo("%d, %d", tempPos.x, tempPos.y)
table.insert(self.pathArry, tempPos)
findPos                     = findPos.parentPoint
end

if table.getn(self.pathArry) < 2 then
isFind                      = false
end

--重置
self.openList                   = {}
self.closeList                  = {}
self.endPos                     = nil
findPos                         = nil
return isFind
end

--获取移动的偏移
function FindPath:getMoveOffset()
local num                       = table.getn(self.pathArry)
if num < 2 then
self:removePath()
return false
end

local curPos                    = self.pathArry[num]
local targetPos                 = self.pathArry[num - 1]
table.remove(self.pathArry, num)

local sPos                      = cc.pSub(curPos, targetPos)
local angle                     = math.atan2(sPos.y, sPos.x) * 180 / math.pi + 180

local direct                    = camera:getDirectionByAngle(angle)
myRole:changeAction(actionList.run, direct)

return cc.pMul(sPos, -1)
end

--移除路径
function FindPath:removePath()
self.pathArry                   = {}
end

--设置角色目标位置
function FindPath:setRolePos(pos)
self.roleTgtPos                 = pos
end

--获取当前路径
function FindPath:getCurPath()
return self.pathArry
end

return FindPath


项目运行示例图:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: