关于A*算法,推荐一篇博客:
莫水千流-A星寻路算法介绍
博客中介绍了A*算法的原理,通过这个探索小游戏实现自动寻路,结合代码加深对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没有更改。