A*寻路算法

实现原理

算法名称: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值,红线是寻路过程。

最终通过父节点还原路径,如下图:(红点表示最终路径)
A*寻路算法_第1张图片

示例代码(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

项目运行示例图:

你可能感兴趣的:(算法,寻路算法,a)