• QQ
  • nahooten@sina.com
  • 常州市九洲新世界花苑15-2

游戏开发

A*算法实现自动寻路

原创内容,转载请注明原文网址:http://homeqin.cn/a/wenzhangboke/jishutiandi/youxikaifa/2018/1014/78.html

常州手游开发培训-A*算法实现自动寻路

 

首先定义了一个Point类:

local Point = class('Point',{})

-- r 行 c 列

function Point:ctor(r,c)

    self.r = r

    self.c = c

end

 

function Point:getR()

    return self.r

end

 

function Point:getC()

    return self.c

end

-- f 权值

function Point:setF(f)

    self.f = f

end

 

function Point:getF()

    return self.f

end

-- p 前继

function Point:setP(p)

    self.p = p

end

 

function Point:getP()

    return self.p

end

 

return Point

这个类保存了r行,c列的点的权值F,最重要的是保存它的父节点(在算法里找到该点的上一个点)。

首先,通过点击地图上的某一瓦片来确定目的地:

function MapEditor:openTouch()

    local visibleRect = cc.rect(self:getPositionX(),self:getPositionY(),self:getMapWidth(),self:getMapHeight())

    local contain = false

    local beginPos

    local function onTouchBegan( touch,event )

        beginPos = touch:getLocation()

        if cc.rectContainsPoint(visibleRect,beginPos) then

            contain = true

        end

        return true  

    end

 

    local function onTouchMoved( touch,event )

    end

 

    local function onTouchEnded( touch,event )

        local endPos = touch:getLocation()

        if math.abs(beginPos.x-endPos.x) < 10 and math.abs(beginPos.y-endPos.y) < 10 then

            if contain and cc.rectContainsPoint(visibleRect,endPos) then

                local posOnMap = self:convertTouchToNodeSpace(touch)

                local R = math.floor(posOnMap.y / self:getRange())

                local C = math.floor(posOnMap.x / self:getRange()) + 1

                R = self.R - R

                self:getParent():autoRoute(R,C)

            end

        end

    end

 

    self.listener = cc.EventListenerTouchOneByOne:create()

    self.listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)

    self.listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)

    self.listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)

 

    self:getEventDispatcher():addEventListenerWithSceneGraphPriority(self.listener,self)

 

    local function onNodeEvent(event)

        if event == 'exit' then

            self:getEventDispatcher():removeEventListener(self.listener)

        end     

    end

    self:registerScriptHandler(onNodeEvent)

end

 

function MapEditor:closeTouch()

    self:getEventDispatcher():removeEventListener(self.listener)

end

如果点中了地图上的某一瓦片,将通过点击的坐标算出这个瓦片的行列。

-- 自动寻路

function MainScene:autoRoute(R,C)

    if self.auto then

        print("--正在自动寻路,无法选择新的目标点。--")

        return 

    end

    -- 原地

    local heroR = self.currentHero:getR()

    local heroC = self.currentHero:getC()

    if heroR == R and heroC == C then 

        print("--当前可行动的英雄所在点--")

        return 

    end

    -- 障碍  有英雄的 不能作为目标点

    local tile = self.mapLayer:getTileByPos(R,C)

    if tonumber(tile:getId()) >= 3 and tonumber(tile:getId()) <= 5 then  -- 障碍

        print("--障碍不能作为目标点--")

        return 

    else 

        for i=1,#self.heros do

            local hero = self.heros[i]

            local _r = hero:getR()

            local _c = hero:getC()

            if _r==R and _c==C then

                print("--有英雄的不能作为目标点--")

                return 

            end

        end

    end

    --

    self.startPoint = require('app/views/Point').new(heroR,heroC)

    self.endPoint = require('app.views.Point').new(R,C)

    self.openList = {}

    self.closeList = {}

    self:beginRoute(self.startPoint)

end

设定障碍和有英雄所在的瓦片不能为目标点,如果正在自动寻路将不能再选择自动寻路的目标点。self.startPoint和self.endPoint是Point类的对象。self.openList保存所有可以考虑的点,self.closeList保存考虑过的点。

function MainScene:beginRoute(point)

    self.auto = true

    if not listContainPoint(self.closeList,point) then

        table.insert(self.closeList,point)

    end

 

    local fourPoints = self:getFourDirPoints(point)

    local num = #self.openList

    for i = 1 , #fourPoints do

        local p = fourPoints[i]

        if isPointEqual(self.endPoint,p) then

            table.insert(self.closeList,p)

            self:findPath()

            return true

        end

        local F = self:getF(p:getR(),p:getC())

        p:setF(F)

        table.insert(self.openList,p)

    end

 

    if num - #self.openList == 0 then

        if #self.openList > 0 then

            local newOne = self.openList[#self.openList]

            table.remove(self.openList,#self.openList)

            return self:beginRoute(newOne) 

        else

            print("--无法自动寻找路径--")

            self.auto = false

            return nil

        end

    end

 

    local ft = {}

    for i = 1 , #self.openList do

        local p = self.openList[i]

        local F = p:getF()

        table.insert(ft,F)

    end

 

    local min = ft[1]

    local minIndex = 1

    for i = 2 , #ft do

        if ft[i] < min then

            min = ft[i]

            minIndex = i

        end

    end

 

    local tmp = self.openList[minIndex]

    table.remove(self.openList,minIndex)

 

    return self:beginRoute(tmp)

end

先从self.startPoint开始,它被加到了self.closeList中,然后通过getFourDirPoints找到这个点上左下右四个方向的点:

function MainScene:getFourDirPoints(point)

    local r = point:getR()

    local c = point:getC()  

    local up = cc.p(r-1,c)

    local left = cc.p(r,c-1)

    local down = cc.p(r+1,c)

    local right = cc.p(r,c+1)

    local t = {up,left,down,right}

    local relt = {}

    for i = 1 , 4 do

        local ccp = t[i]

        if isPointEqualXY(self.endPoint,ccp) then

            self.endPoint:setP(point)

            table.insert(relt,1,self.endPoint)

            return relt

        end

        if self:PointCanOpen(ccp) then

            -- 在openList中的已经有父节点

            if not listContainObj(self.openList,ccp) then

                local p = require('app/views/Point').new(ccp.x,ccp.y)

                p:setP(point)

                table.insert(relt,p)

            end

        end

    end

    return relt

end

上左下右四个点是cc.p()对象,并不是Point对象,如果这个点就是选择的终点,那么指定终点的父节点,并返回:

        if isPointEqualXY(self.endPoint,ccp) then

            self.endPoint:setP(point)

            table.insert(relt,1,self.endPoint)

            return relt

        end

在终点还没出现在查找范围内时,如果符合考虑的条件,那么用这个点的行列创建一个Point对象,并指定父节点:

        if self:PointCanOpen(ccp) then

            -- 在openList中的已经有父节点

            if not listContainObj(self.openList,ccp) then

                local p = require('app/views/Point').new(ccp.x,ccp.y)

                p:setP(point)

                table.insert(relt,p)

            end

        end

如果这个点在这次查询中已经在self.openList中(上几次查询被加入),那么不再考虑,因为这个点已经被指定了父节点。

考虑条件:

function MainScene:PointCanOpen(ccp)

    local tile = self.mapLayer:getTileByPos(ccp.x,ccp.y)

    if not tile then

        return false

    end 

    if tonumber(tile:getId()) >= 3 and tonumber(tile:getId()) <= 5 then  -- 障碍

        return false

    elseif tonumber(tile:getId()) == 2 then -- 陷阱

        return false

    elseif listContainObj(self.closeList,ccp) then -- 在close表中

        return false

    else  -- 有英雄

        for i=1,#self.heros do

            local hero = self.heros[i]

            local _r = hero:getR()

            local _c = hero:getC()

            if _r==ccp.x and _c==ccp.y then

                return false

            end

        end

    end

    return true

end

可以考虑的点已经创建了Point对象加入到了relt表中并返回,遍历返回的可以考虑的点:

    local fourPoints = self:getFourDirPoints(point)

    local num = #self.openList

    for i = 1 , #fourPoints do

        local p = fourPoints[i]

        if isPointEqual(self.endPoint,p) then

            table.insert(self.closeList,p)

            self:findPath()

            return true

        end

        local F = self:getF(p:getR(),p:getC())

        p:setF(F)

        table.insert(self.openList,p)

    end

 

    if num - #self.openList == 0 then

        if #self.openList > 0 then

            local newOne = self.openList[#self.openList]

            table.remove(self.openList,#self.openList)

            return self:beginRoute(newOne) 

        else

            print("--无法自动寻找路径--")

            self.auto = false

            return nil

        end

    end

之前查询中如果找到了终点,那么这里将终点插入到self.closeList中,结束查询,因为已经找到了路径。如果没有,则计算它们的权值F,并插入到self.openList中。

function MainScene:getG(r,c)

    -- 从起点到该点需要几步

    return math.abs(self.startPoint:getC() - c) +  math.abs(self.startPoint:getR() - r)

end

 

function MainScene:getH(r,c)

    -- 该点到终点的估算值

    return math.abs(self.endPoint:getR() - r) + math.abs(self.endPoint:getC() - c)

end

 

function MainScene:getF(r,c)

    -- F = G + H

    return self:getG(r,c) + self:getH(r,c)

end

变量num记录了这次查询可考虑点之前的self.openList中的元素个数,如果这次查询没有找到符合条件的点(getFourDirPoints方法返回的表中没有元素),num - #self.openList == 0,那么说明按照目前查询的路径,已经没有路可走,那么就废弃这条路线,从self.openList找一个新的点重新查询(最后一个,最近被加入),这个点也将从self.openList中移除。如果self.openList中没有元素,说明没有路径可以到达选择的目标点。

self.openList中有新加入的可考虑点,那么找出其中权值F最小的点,从这个点开始新一轮的查询:

    local ft = {}

    for i = 1 , #self.openList do

        local p = self.openList[i]

        local F = p:getF()

        table.insert(ft,F)

    end

 

    local min = ft[1]

    local minIndex = 1

    for i = 2 , #ft do

        if ft[i] < min then

            min = ft[i]

            minIndex = i

        end

    end

 

    local tmp = self.openList[minIndex]

    table.remove(self.openList,minIndex)

 

    return self:beginRoute(tmp)

通过函数的迭代,不断查询,直到终点出现在可考虑范围,getFourDirPoints方法中:

        if isPointEqualXY(self.endPoint,ccp) then

            self.endPoint:setP(point)

            table.insert(relt,1,self.endPoint)

            return relt

        end

对返回的relt表的遍历发现终点,就已经找到了路径:

        if isPointEqual(self.endPoint,p) then

            table.insert(self.closeList,p)

            self:findPath()

            return true

        end

用self.pathList保存路径点:

function MainScene:findPath()

    self.pathList = {}

    table.insert(self.pathList,self.closeList[#self.closeList])

    self:getPointParent(self.endPoint)

end

  • 先将self.closeList的最后一个元素也就是终点插入self.pathList中,然后就是不断查找父节点的过程,终点在之前的查找可考虑点时被指定了父节点,每个考虑的都有唯一的父节点,那么不断反向查询,直到发现一个点的父节点是起点时,就找到了一条完整的路径:

function MainScene:getPointParent(point)

    local parent = point:getP()

    if parent then

        table.insert(self.pathList,1,parent)

        if isPointEqual(self.startPoint,parent) then

            self:getActionDirection()

            return 

        else

            self:getPointParent(parent)

        end

    else

        print('--有一个Point对象没有父节点--')

    end

end

根据之前手动操作的玩法,得到从这条路径行走每一步的前进的方向:

function MainScene:getActionDirection()

    local dir = {}

    for i = 1 , #self.pathList do

        local direction 

        if i ~= #self.pathList then

            local p1 = self.pathList[i]

            local p2 = self.pathList[i+1]

            if p1:getR() == p2:getR() then  -- 左右

                if p1:getC() < p2:getC() then

                    direction = 'right'           

                else

                    direction = 'left'

                end

            else  -- 上下

                if p1:getR() > p2:getR() then

                    direction = 'up'

                else

                    direction = 'down'              

                end

            end

        end

        table.insert(dir,direction)

    end

    self:placeArrow(dir)

end

为了方便直观,通过placeArrow方法添加方向指引的箭头,体力足够到达的瓦片放置绿色箭头,不够到达的放置红色箭头:

function MainScene:placeArrow(dir)

    self.rbas = {}

    for i = 1 , #dir do

        local direction = dir[i]

        local point = self.pathList[i+1]

        local img 

        if self.currentHero:getMyTl() >= 5 * i then

            if i == #dir then

                img = 'reach.png'

            else

                img = 'dir.png'

            end

        else

            if i == #dir then

                img = 'reach_n.png'

            else

                img = 'dir_n.png'

            end

        end

        local sp = cc.Sprite:create(img)

        if i ~= #dir then

            if direction == 'right' then

                sp:setFlippedX(true)

            elseif direction == 'up' then

                sp:setRotation(90)

            elseif direction == 'down' then

                sp:setRotation(-90)

            end

        end

        table.insert(self.rbas,sp)

        local tile = self.mapLayer:getTileByPos(point:getR(),point:getC())

        self.mapLayer:addChild(sp)

        sp:setPosition(tile:getPositionX(),tile:getPositionY()+tile:getNodeSize().height/2)

    end

    self:playAction(dir,1)

end

最后就是根据路径进行英雄的自动行走,在自动寻路前进时不显示手动操作的箭头:

function MainScene:playAction(dir,start)

    if dir[start] then

        local isContinue = true

        local seq = cc.Sequence:create(cc.CallFunc:create(function()

            isContinue = self:heroAction(self.currentHero,dir[start],true)

            if start == #dir then

                print("--到达指定地点--")

            end

            start = start + 1

        end),cc.DelayTime:create(1),cc.CallFunc:create(function() 

            if isContinue then

                local sp = self.rbas[1]

                sp:removeFromParent()

                table.remove(self.rbas,1)

                self:playAction(dir,start)

            else

                self.auto = false

                self:showArrow(self.currentHero)

                self:stopAction(self.routeAction)

                for i = 1 , #self.rbas do

                    local sp = self.rbas[#self.rbas]

                    sp:removeFromParent()

                    table.remove(self.rbas,#self.rbas)

                end

            end

        end))

        self.routeAction = self:runAction(seq)

    else

        self.auto = false

        self:showArrow(self.currentHero)

        self:stopAction(self.routeAction)

    end

end

  • self:runAction(seq),顺序执行动作,self:heroAction(self.currentHero,dir[start],true)返回的isContinue为true就还有体力前进,每走一步删除一个指引箭头。isContinue为false时,没有体力前进,那么显示手动操作的箭头,并删除指引箭头。

运行效果:

 

 

 

MainScene完整代码:

local MainScene = class("MainScene", cc.load("mvc").ViewBase)

 

function MainScene:ctor()

    self.heros = {}

    self.auto = false

    self:addMap()

    self:addHero()

end

 

function MainScene:addMap()

    local mapDataStr = cc.FileUtils:getInstance():getStringFromFile('res/MapData.json')

    local mapData = json.decode(mapDataStr)

 

    local mapStr = mapData['map']

    local mapInfo = self.split(mapStr,'#')

 

    self.mapLayer = require('app/views/MapEditor').new(mapInfo)

    self:addChild(self.mapLayer)

    self.range = self.mapLayer:getRange()

 

    local winSize = cc.Director:getInstance():getWinSize()

    self.mapLayer:setPosition(winSize.width/2-self.mapLayer:getMapWidth()/2,winSize.height/2-self.mapLayer:getMapHeight()/2)

    self.mapLayer:openTouch()

end

 

function MainScene:addHero()

    local heroDataStr = cc.FileUtils:getInstance():getStringFromFile('HeroData.json')

    local heroData = json.decode(heroDataStr)

    local data = heroData['data']

 

    ccs.ArmatureDataManager:getInstance():addArmatureFileInfo('qishi.ExportJson')

    for i=1,#data do

        local hero = require('app/views/Hero').new(data[i]['id'])

        hero:setR(data[i]['r'])

        hero:setC(data[i]['c'])

        -- 设定初始体力为100

        hero:setMyTl(100)

        self.mapLayer:addChild(hero,50)

        local tile = self.mapLayer:getTileByPos(data[i]['r'],data[i]['c'])

        hero:setPosition(tile:getPosition())

        table.insert(self.heros,hero)

        local isShow = false

        if i == 1 then

            isShow = true

            self.currentHero = hero

        end

        self:addArrow(hero,isShow)

    end

end

 

function MainScene:addArrow(hero,isShow)

    local arrow = require('app/views/Arrow').new(hero:getNodeSize().width)

    hero:addChild(arrow)

    arrow:setPositionY(hero:getNodeSize().height/2)

    arrow:setName('Arrow')

    -- 检查箭头该不该显示

    if isShow then

        self:showArrow(hero)

    else

        arrow:showSomeone('all',false)

    end

    arrow:setBtnFunc(function(sender,_type)

        if _type == ccui.TouchEventType.ended then

            self:heroAction(hero,sender:getName())                               

        end

    end)

end

 

function MainScene:showArrow(hero)

    local arr = hero:getChildByName('Arrow')

 

    local id = hero:getMyId()

    local heroR = hero:getR()

    local heroC = hero:getC()

 

    local mapC = self.mapLayer:getC()

    local mapR = self.mapLayer:getR()

 

    if heroC==1 then

        arr:showSomeone('left',false)

    elseif heroC==mapC then

        arr:showSomeone('right',false)

    end

 

    if heroC-1>0 then

        arr:showSomeone('left',self:checkArrowShow(heroR,heroC-1,id))

    end

 

    if heroC+1<=mapC then

        arr:showSomeone('right',self:checkArrowShow(heroR,heroC+1,id))

    end

 

    if heroR==1 then

        arr:showSomeone('up',false)

    elseif heroR==mapR then

        arr:showSomeone('down',false)

    end

 

    if heroR-1>0 then

        arr:showSomeone('up',self:checkArrowShow(heroR-1,heroC,id))

    end

 

    if heroR+1<=mapR then

        arr:showSomeone('down',self:checkArrowShow(heroR+1,heroC,id))

    end

end

 

function MainScene:checkArrowShow(r,c,id)

    local tile  = self.mapLayer:getTileByPos(r,c)

    local have = false

    if tonumber(tile:getId()) >= 3 and tonumber(tile:getId()) <= 5 then  -- 障碍不可站 

        return false

    else

        for i=1,#self.heros do

            local hero = self.heros[i]

            if hero:getMyId() ~= id then

                local _r = hero:getR()

                local _c = hero:getC()

                if _r == r and _c == c then

                    have = true

                    break

                end

            end

        end

        if have then

            return false

        else

            return true

        end

    end

end

 

function MainScene:heroAction(hero,arr_dir,isAuto)

    local heroR = hero:getR()

    local heroC = hero:getC()

    local arrow = hero:getChildByName('Arrow')

    local heroTl = hero:getMyTl()

    local heroX,heroY = hero:getPosition()

 

    if heroTl <= 0 then

        print("--体力不足--")

        return false

    end

 

    if arr_dir == 'left' then

        heroX = heroX - self.range

        heroC = heroC - 1 

    elseif arr_dir == 'up' then

        heroY = heroY + self.range

        heroR = heroR - 1

    elseif arr_dir == 'right' then

        heroX = heroX + self.range

        heroC = heroC + 1

    else  -- down

        heroY = heroY - self.range

        heroR = heroR + 1 

    end

 

    if heroTl >= 5 then

        if arr_dir == 'left' or arr_dir == 'right' then

            hero:setDir(arr_dir)

        end

 

        local move = cc.MoveTo:create(0.7,cc.p(heroX,heroY))

        local function playA()

            arrow:showSomeone('all',false)

            hero:playAnimation('move')

        end

        local spawn = cc.Spawn:create(move,cc.CallFunc:create(playA))

 

        local function playB()

            hero:playAnimation('stand')

 

            hero:setR(heroR)

            hero:setC(heroC)

 

            if not isAuto then

                self:showArrow(hero)

            end

 

            hero:setMyTl(heroTl - 5)

        end

        hero:runAction(cc.Sequence:create(spawn,cc.CallFunc:create(playB)))

        return true

    end

end

 

-- 自动寻路

function MainScene:autoRoute(R,C)

    if self.auto then

        print("--正在自动寻路,无法选择新的目标点。--")

        return 

    end

    -- 原地

    local heroR = self.currentHero:getR()

    local heroC = self.currentHero:getC()

    if heroR == R and heroC == C then 

        print("--当前可行动的英雄所在点--")

        return 

    end

    -- 障碍  有英雄的 不能作为目标点

    local tile = self.mapLayer:getTileByPos(R,C)

    if tonumber(tile:getId()) >= 3 and tonumber(tile:getId()) <= 5 then  -- 障碍

        print("--障碍不能作为目标点--")

        return 

    else 

        for i=1,#self.heros do

            local hero = self.heros[i]

            local _r = hero:getR()

            local _c = hero:getC()

            if _r==R and _c==C then

                print("--有英雄的不能作为目标点--")

                return 

            end

        end

    end

    --

    self.startPoint = require('app/views/Point').new(heroR,heroC)

    self.endPoint = require('app.views.Point').new(R,C)

    self.openList = {}

    self.closeList = {}

    self:beginRoute(self.startPoint)

end

 

local function isPointEqualXY(point,ccp)

    if point:getR() == ccp.x and point:getC() == ccp.y then

        return true

    end

    return false

end

 

local function isPointEqual(point1,point2)

    if point1:getR() == point2:getR() and point1:getC() == point2:getC() then

        return true

    end

    return false

end

 

local function listContainPoint(list,point)

    if #list > 0 then

        for i = 1 , #list do

            local tmp = list[i]

            if tmp:getR() == point:getR() and tmp:getC() == point:getC() then

                return true

            end

        end

    end

    return false

end

 

local function listContainObj(list,obj)

    if #list > 0 then

        for i = 1 , #list do

            local tmp = list[i]

            if tmp:getR() == obj.x and tmp:getC() == obj.y then

                return true

            end

        end

    end

    return false

end

 

function MainScene:PointCanOpen(ccp)

    local tile = self.mapLayer:getTileByPos(ccp.x,ccp.y)

    if not tile then

        return false

    end 

    if tonumber(tile:getId()) >= 3 and tonumber(tile:getId()) <= 5 then  -- 障碍

        return false

    elseif tonumber(tile:getId()) == 2 then -- 陷阱

        return false

    elseif listContainObj(self.closeList,ccp) then -- 在close表中

        return false

    else  -- 有英雄

        for i=1,#self.heros do

            local hero = self.heros[i]

            local _r = hero:getR()

            local _c = hero:getC()

            if _r==ccp.x and _c==ccp.y then

                return false

            end

        end

    end

    return true

end

 

function MainScene:getFourDirPoints(point)

    local r = point:getR()

    local c = point:getC()  

    local up = cc.p(r-1,c)

    local left = cc.p(r,c-1)

    local down = cc.p(r+1,c)

    local right = cc.p(r,c+1)

    local t = {up,left,down,right}

    local relt = {}

    for i = 1 , 4 do

        local ccp = t[i]

        if isPointEqualXY(self.endPoint,ccp) then

            self.endPoint:setP(point)

            table.insert(relt,1,self.endPoint)

            return relt

        end

        if self:PointCanOpen(ccp) then

            -- 在openList中的已经有父节点

            if not listContainObj(self.openList,ccp) then

                local p = require('app/views/Point').new(ccp.x,ccp.y)

                p:setP(point)

                table.insert(relt,p)

            end

        end

    end

    return relt

end

 

function MainScene:getG(r,c)

    -- 从起点到该点需要几步

    return math.abs(self.startPoint:getC() - c) +  math.abs(self.startPoint:getR() - r)

end

 

function MainScene:getH(r,c)

    -- 该点到终点的估算值

    return math.abs(self.endPoint:getR() - r) + math.abs(self.endPoint:getC() - c)

end

 

function MainScene:getF(r,c)

    -- F = G + H

    return self:getG(r,c) + self:getH(r,c)

end

 

function MainScene:beginRoute(point)

    self.auto = true

    if not listContainPoint(self.closeList,point) then

        table.insert(self.closeList,point)

    end

 

    local fourPoints = self:getFourDirPoints(point)

    local num = #self.openList

    for i = 1 , #fourPoints do

        local p = fourPoints[i]

        if isPointEqual(self.endPoint,p) then

            table.insert(self.closeList,p)

            self:findPath()

            return true

        end

        local F = self:getF(p:getR(),p:getC())

        p:setF(F)

        table.insert(self.openList,p)

    end

 

    if num - #self.openList == 0 then

        if #self.openList > 0 then

            local newOne = self.openList[#self.openList]

            table.remove(self.openList,#self.openList)

            return self:beginRoute(newOne) 

        else

            print("--无法自动寻找路径--")

            self.auto = false

            return nil

        end

    end

 

    local ft = {}

    for i = 1 , #self.openList do

        local p = self.openList[i]

        local F = p:getF()

        table.insert(ft,F)

    end

 

    local min = ft[1]

    local minIndex = 1

    for i = 2 , #ft do

        if ft[i] < min then

            min = ft[i]

            minIndex = i

        end

    end

 

    local tmp = self.openList[minIndex]

    table.remove(self.openList,minIndex)

 

    return self:beginRoute(tmp)

end

 

function MainScene:findPath()

    self.pathList = {}

    table.insert(self.pathList,self.closeList[#self.closeList])

    self:getPointParent(self.endPoint)

end

 

function MainScene:getPointParent(point)

    local parent = point:getP()

    if parent then

        table.insert(self.pathList,1,parent)

        if isPointEqual(self.startPoint,parent) then

            self:getActionDirection()

            return 

        else

            self:getPointParent(parent)

        end

    else

        print('--有一个Point对象没有父节点--')

    end

end

 

function MainScene:getActionDirection()

    local dir = {}

    for i = 1 , #self.pathList do

        local direction 

        if i ~= #self.pathList then

            local p1 = self.pathList[i]

            local p2 = self.pathList[i+1]

            if p1:getR() == p2:getR() then  -- 左右

                if p1:getC() < p2:getC() then

                    direction = 'right'           

                else

                    direction = 'left'

                end

            else  -- 上下

                if p1:getR() > p2:getR() then

                    direction = 'up'

                else

                    direction = 'down'              

                end

            end

        end

        table.insert(dir,direction)

    end

    self:placeArrow(dir)

end

 

function MainScene:placeArrow(dir)

    self.rbas = {}

    for i = 1 , #dir do

        local direction = dir[i]

        local point = self.pathList[i+1]

        local img 

        if self.currentHero:getMyTl() >= 5 * i then

            if i == #dir then

                img = 'reach.png'

            else

                img = 'dir.png'

            end

        else

            if i == #dir then

                img = 'reach_n.png'

            else

                img = 'dir_n.png'

            end

        end

        local sp = cc.Sprite:create(img)

        if i ~= #dir then

            if direction == 'right' then

                sp:setFlippedX(true)

            elseif direction == 'up' then

                sp:setRotation(90)

            elseif direction == 'down' then

                sp:setRotation(-90)

            end

        end

        table.insert(self.rbas,sp)

        local tile = self.mapLayer:getTileByPos(point:getR(),point:getC())

        self.mapLayer:addChild(sp)

        sp:setPosition(tile:getPositionX(),tile:getPositionY()+tile:getNodeSize().height/2)

    end

    self:playAction(dir,1)

end

 

function MainScene:playAction(dir,start)

    if dir[start] then

        local isContinue = true

        local seq = cc.Sequence:create(cc.CallFunc:create(function()

            isContinue = self:heroAction(self.currentHero,dir[start],true)

            if start == #dir then

                print("--到达指定地点--")

            end

            start = start + 1

        end),cc.DelayTime:create(1),cc.CallFunc:create(function() 

            if isContinue then

                local sp = self.rbas[1]

                sp:removeFromParent()

                table.remove(self.rbas,1)

                self:playAction(dir,start)

            else

                self.auto = false

                self:showArrow(self.currentHero)

                self:stopAction(self.routeAction)

                for i = 1 , #self.rbas do

                    local sp = self.rbas[#self.rbas]

                    sp:removeFromParent()

                    table.remove(self.rbas,#self.rbas)

                end

            end

        end))

        self.routeAction = self:runAction(seq)

    else

        self.auto = false

        self:showArrow(self.currentHero)

        self:stopAction(self.routeAction)

    end

end

 

function MainScene.split(str,reps)

    local resultStrList = {}

    string.gsub(str,'[^'..reps..']+',function (w)

        table.insert(resultStrList,w)

    end)

    return resultStrList

end

 

return MainScene

  • MapEditor完整代码:

local MapEditor = class('MapEditor',function () 

    return cc.Layer:create() 

end)

 

function MapEditor:ctor(mapInfo)

    -- 行数

    self.R = #mapInfo

 

    local tileDataStr = cc.FileUtils:getInstance():getStringFromFile('res/TileData.json')

    local tileData = json.decode(tileDataStr)

 

    for i=1,#mapInfo do

        local c = self.split(mapInfo[i],',')

        for j=1,#c do

            local img = tileData[c[j]]

            local tile = require('app/views/TileNode').new(c[j],img)

 

            if i==1 and j==1 then

                -- 列数

                self.C = #c

                self:setMapWidth(tile:getNodeSize().width * #c)

                self:setMapHeight(tile:getNodeSize().height * #mapInfo)

                self:setRange(tile:getNodeSize().width)

            end

 

            self:addChild(tile)

 

            local _x = (j*2-1) * (tile:getNodeSize().width/2)

            local _y = (#mapInfo-i) * (tile:getNodeSize().height)

 

            tile:setPosition(_x,_y)

            tile:setMyTag(i,j)

            tile:setName('Tile')

        end

    end

end

 

function MapEditor.split(str,reps)

    local resultStrList = {}

    string.gsub(str,'[^'..reps..']+',function (w)

        table.insert(resultStrList,w)

    end)

    return resultStrList

end

 

function MapEditor:setMapWidth(_w)

    self.mapWidth = _w

end

 

function MapEditor:setMapHeight(_h)

    self.mapHeight = _h 

end

 

function MapEditor:getMapWidth()

    return self.mapWidth

end

 

function MapEditor:getMapHeight()

    return self.mapHeight

end

 

function MapEditor:getR()

    return self.R

end

 

function MapEditor:getC()

    return self.C

end

 

function MapEditor:setRange(_range)

    self.range = _range

end

 

function MapEditor:getRange()

    return self.range

end

 

function MapEditor:getTileByPos(_x,_y)

    local chs = self:getChildren()

    for i=1,#chs do

        local t = chs[i]

        if t:getName()=='Tile' then

            local r,c = t:getMyTag()

            if r==_x and c==_y then

                return t

            end

        end

    end

end

 

function MapEditor:openTouch()

    local visibleRect = cc.rect(self:getPositionX(),self:getPositionY(),self:getMapWidth(),self:getMapHeight())

    local contain = false

    local beginPos

    local function onTouchBegan( touch,event )

        beginPos = touch:getLocation()

        if cc.rectContainsPoint(visibleRect,beginPos) then

            contain = true

        end

        return true  

    end

 

    local function onTouchMoved( touch,event )

    end

 

    local function onTouchEnded( touch,event )

        local endPos = touch:getLocation()

        if math.abs(beginPos.x-endPos.x) < 10 and math.abs(beginPos.y-endPos.y) < 10 then

            if contain and cc.rectContainsPoint(visibleRect,endPos) then

                local posOnMap = self:convertTouchToNodeSpace(touch)

                local R = math.floor(posOnMap.y / self:getRange())

                local C = math.floor(posOnMap.x / self:getRange()) + 1

                R = self.R - R

                self:getParent():autoRoute(R,C)

            end

        end

    end

 

    self.listener = cc.EventListenerTouchOneByOne:create()

    self.listener:registerScriptHandler(onTouchBegan,cc.Handler.EVENT_TOUCH_BEGAN)

    self.listener:registerScriptHandler(onTouchMoved,cc.Handler.EVENT_TOUCH_MOVED)

    self.listener:registerScriptHandler(onTouchEnded,cc.Handler.EVENT_TOUCH_ENDED)

 

    self:getEventDispatcher():addEventListenerWithSceneGraphPriority(self.listener,self)

 

    local function onNodeEvent(event)

        if event == 'exit' then

            self:getEventDispatcher():removeEventListener(self.listener)

        end     

    end

    self:registerScriptHandler(onNodeEvent)

end

 

function MapEditor:closeTouch()

    self:getEventDispatcher():removeEventListener(self.listener)

end

 

return MapEditor

  • Arrow、Hero、TileNode没有更改。


上篇:上一篇:UGUI下拉列表的封装
下篇:下一篇:Unity中创建单例类基类