CocosLua3.10 A星算法(寻路算法)
2019-12-03 本文已影响0人
叫我29就好啦
参考文章:https://blog.csdn.net/zhulichen/article/details/78786493
主场景
local GameLayer = require('app.testFile.event.Key_5_Event_File.Layer.GameLayer')
local event = class('event', cc.Node)
function event:ctor()
self:initValue()
self:initLayer()
end
function event:initValue()
self._GameLayer = nil
end
function event:initLayer()
self._GameLayer = GameLayer:create()
self:addChild(self._GameLayer)
self._GameLayer:createLayer()
end
return event
创建界面
通过createModelData()方法,创建模板地图,标记的值为方块的类型
1.普通方块
2.障碍方块
3.起点
4.终点
这儿是由上至下的顺序,即左上角为(1,1),然后根据方块的大小和定好的起始坐标来创建单个方块。
local GameNode = require('app.testFile.event.Key_5_Event_File.Node.GameNode')
local RoadCheck = require('app.testFile.event.Key_5_Event_File.Data.RoadCheck')
local Row = 12 -- 行
local Column = 8 -- 列
local Interval = 70 -- 区块间隔
local Start_Pos = cc.p(40, display.height) --起始坐标
local TurnRound = 2 -- 限制转弯次数
local GameLayer = class('GameLayer', cc.Node)
function GameLayer:ctor()
self:initValue()
self:initData()
end
function GameLayer:initValue()
self._modelTable = {} -- 模板表(地图表)
self._nodeTable = {}
self._roadCheck = nil
self._startPoint = cc.p(0, 0)
self._endPoint = cc.p(0, 0)
end
function GameLayer:initData()
self._roadCheck = RoadCheck:create(self, {Row = Row, Column = Column, TurnRound = TurnRound})
self:addChild(self._roadCheck)
end
function GameLayer:createLayer()
self._modelTable = self:createModelData()
self:createNode()
self._roadCheck:startCheck(self._startPoint, self._endPoint)
end
function GameLayer:createNode()
if not self._modelTable or #self._modelTable <= 0 then
return
end
for i, v in ipairs(self._modelTable) do
local x = (i % Row == 0) and Row or i % Row
local y = (i % Row == 0) and (i / Row) or math.floor(i / Row + 1)
local data = {Value = v, Pos = cc.p(x, y)}
local node = GameNode:create(data)
self:addChild(node, 5)
node:createNode()
local str = string.format('%s_%s', x, y)
self._nodeTable[str] = node
if v == 3 then
self._startPoint = cc.p(x, y)
elseif v == 4 then
self._endPoint = cc.p(x, y)
end
end
end
function GameLayer:getNodeByPos(_pos)
local str = string.format('%s_%s', _pos.x, _pos.y)
return self._nodeTable[str]
end
function GameLayer:createModelData()
local temp = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1,
1, 3, 1, 1, 1, 2, 1, 1, 1, 1, 4, 1,
1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
}
return temp
end
function GameLayer:getStartPos()
return Start_Pos
end
function GameLayer:getInterval()
return Interval
end
return GameLayer
单个方块
这儿将4种方块定为4种颜色,并另外新增两个颜色用于测试寻路和最终结果
--[[
Value = (数值,方块的类型),
Pos = (位置),
]]
local NodeSize = cc.size(60, 60) -- 形块大小
local GameNode = class('GameNode', cc.Node)
local Color_Normal = cc.c3b(80, 80, 80)
local Color_Obstacle = cc.c3b(0, 0, 255)
local Color_Start = cc.c3b(255, 0, 0)
local Color_End = cc.c3b(0, 255, 0)
local Color_IsChecked = cc.c3b(255, 0, 0)
local Color_IsThroughed = cc.c3b(0, 255, 0)
local ColorTable = {
Color_Normal,
Color_Obstacle,
Color_Start,
Color_End,
}
function GameNode:ctor(data)
self._data = data
self:initValue()
end
function GameNode:initValue()
self._layout = nil
self._Value = self._data.Value or 1
self._Pos = self._data.Pos or cc.p(0, 0)
self._parentPos = cc.p(0, 0)
self._text_F = nil
self._text_G = nil
self._text_H = nil
self._value_G = 0
self._value_H = 0
self._value_F = self._value_G + self._value_H
end
function GameNode:createNode()
self._layout = self:initNode(false)
self:resetPosition()
if self._Value == 1 then
-- self:setText(2, 2)
-- self:showText()
end
end
function GameNode:initNode(isShowText)
local layout = ccui.Layout:create()
layout:setAnchorPoint(cc.p(0.5, 0.5))
layout:setContentSize(NodeSize)
layout:setBackGroundColorType(ccui.LayoutBackGroundColorType.solid) --设置颜色
layout:setBackGroundColor(ColorTable[self._Value])
self:addChild(layout)
if isShowText then
local text = ccui.Text:create()
text:setString(string.format('%s', self._Value))
text:setFontSize(32)
self:addChild(text, 2)
end
return layout
end
function GameNode:setPosition(pos)
if not pos or not pos.x or not pos.y then
return
end
self._Pos = pos
end
function GameNode:getPosition()
return self._Pos
end
function GameNode:resetPosition()
self:setPositionX(self:getParent():getStartPos().x + self:getParent():getInterval() * self._Pos.x)
self:setPositionY(self:getParent():getStartPos().y - self:getParent():getInterval() * self._Pos.y)
end
function GameNode:showTextF(str)
if not self._text_F then
self._text_F = ccui.Text:create()
self._text_F:setFontSize(18)
self._text_F:setPosition(-self._layout:getContentSize().width/2 + 6, self._layout:getContentSize().height/2 - 8)
self:addChild(self._text_F)
end
self._text_F:setString(string.format('%s', str))
end
function GameNode:showTextG(str)
if not self._text_G then
self._text_G = ccui.Text:create()
self._text_G:setFontSize(18)
self._text_G:setPosition(-self._layout:getContentSize().width/2 + 6, -self._layout:getContentSize().height/2 + 8)
self:addChild(self._text_G)
end
self._text_G:setString(string.format('%s', str))
end
function GameNode:showTextH(str)
if not self._text_H then
self._text_H = ccui.Text:create()
self._text_H:setFontSize(18)
self._text_H:setPosition(self._layout:getContentSize().width/2 - 6, -self._layout:getContentSize().height/2 + 8)
self:addChild(self._text_H)
end
self._text_H:setString(string.format('%s', str))
end
function GameNode:showText()
self:showTextG(self._value_G)
self:showTextH(self._value_H)
self:showTextF(self._value_F)
end
function GameNode:setText(_g, _h)
self._value_G, self._value_H, self._value_F = _g, _h, _g+_h
self:showText()
end
function GameNode:getFValue()
return self._value_F
end
function GameNode:getGValue()
return self._value_G
end
-- 是否可以经过该点(即这个是不是障碍)
function GameNode:getIsCanMove()
return self._Value == 1 or self._Value == 3 or self._Value == 4
end
function GameNode:setIsCheckedColor()
self._layout:setBackGroundColor(Color_IsChecked)
end
function GameNode:setIsThroughedColor()
self._layout:setBackGroundColor(Color_IsThroughed)
end
function GameNode:setParentPos(pos)
self._parentPos = pos
end
function GameNode:getParentPos()
return self._parentPos
end
return GameNode
寻路算法
OneRoadValue即方块间的权值,这里恒定为10。这儿只搜寻上下左右方向,不考虑斜线行走。
自我理解:获得当前的点,将定好的周围的点放于待查列表中(openList)(这里需要判断之前是否处于openList,有额外的特殊处理),设置好父节点,并将当前点放于不需要查的列表中(closeList),每次完成上面顺序后,获得当前openList最小的F值,并递归此方法,参数为最小的F值对应的节点(这里运用的是点,因人而异)。之后,如果将终点放置于openList中,则认为已获得结果。最后从终点依次获得父节点,得到终点->起点的顺序。
local OneRoadValue = 10
local RoadCheck = class('RoadCheck', cc.Node)
function RoadCheck:ctor(delegate, data)
self._delegate = delegate
self._startPos = nil
self._endPos = nil
self._openList = {}
self._closeList = {}
end
function RoadCheck:startCheck(_startPos, _endPos)
self._startPos = _startPos
self._endPos = _endPos
self:checkDiretion(self._startPos)
end
function RoadCheck:addToOpenTable(pos)
local str = string.format('%s_%s', pos.x, pos.y)
self._openList[str] = pos
end
function RoadCheck:checkIsInOpenTable(pos)
local str = string.format('%s_%s', pos.x, pos.y)
return self._openList[str]
end
function RoadCheck:removeInOpenTable(pos)
local str = string.format('%s_%s', pos.x, pos.y)
self._openList[str] = nil
end
function RoadCheck:addToCloseTable(pos)
local str = string.format('%s_%s', pos.x, pos.y)
self._closeList[str] = true
end
function RoadCheck:checkIsInCloseTable(pos)
local str = string.format('%s_%s', pos.x, pos.y)
return self._closeList[str]
end
-- 找到open表中最小的f值
function RoadCheck:getMinFPosInOpenTable()
if table.nums(self._openList) == 0 then
return false
end
local minPos = cc.p(0, 0)
local minF = 99999
for i, v in pairs(self._openList) do
local node = self._delegate:getNodeByPos(v)
if node:getFValue() < minF then
minF = node:getFValue()
minPos = v
end
end
return minPos
end
-- 获得当前node的g值,根据当前寻路的节点获得
function RoadCheck:getGValueInThroughRoad(pPos)
local gValue = OneRoadValue
local node = self._delegate:getNodeByPos(pPos)
gValue = gValue + node:getGValue()
return gValue
end
function RoadCheck:isInEnd()
return self:checkIsInOpenTable(self._endPos)
end
function RoadCheck:showRoad()
local pos = self._endPos
local action = nil
local function fun()
local node = self._delegate:getNodeByPos(pos)
local pPos = node:getParentPos()
local pNode = self._delegate:getNodeByPos(pPos)
pNode:setIsThroughedColor()
if pPos.x == self._startPos.x and pPos.y == self._startPos.y then
if action then
self:stopAction(action)
end
return
end
pos = pNode:getPosition()
-- 不带延迟调用
-- fun()
end
-- 带延迟调用
action = schedule(self, fun, 0.2)
-- 不带延迟调用
-- fun()
end
-- 添加到open表内
function RoadCheck:checkDiretion(currentPos)
if self:isInEnd() then -- 如果到达了终点
self:showRoad()
return
end
-- 添加到closeList,在openList中移除,插入到遍历表中
self:addToCloseTable(currentPos)
self:removeInOpenTable(currentPos)
-- 改变当前的颜色
local cNode = self._delegate:getNodeByPos(currentPos)
cNode:setIsCheckedColor()
local x, y = currentPos.x, currentPos.y
local temp = {cc.p(x, y+1), cc.p(x, y-1), cc.p(x-1, y), cc.p(x+1, y)} -- 上下左右
for i, v in ipairs(temp) do
local node = self._delegate:getNodeByPos(v)
if node and node:getIsCanMove() and not self:checkIsInCloseTable(v) then -- 如果有这个节点,并且可经过,并且不处于closelist里面,则放到openlist里面
-- 检查是否在openlist中
if self:checkIsInOpenTable(v) then
if self:getGValueInThroughRoad(currentPos) < node:getGValue() then -- 如果现在经过的点,比之前经过的点,G值还小,重新设置父节点和FGH值
node:setParentPos(currentPos)
local g = self:getGValueInThroughRoad(currentPos)
local h = (math.abs(v.x - self._endPos.x) + math.abs(v.y - self._endPos.y)) * OneRoadValue
node:setText(g, h)
end
else
self:addToOpenTable(v)
node:setParentPos(currentPos)
local g = self:getGValueInThroughRoad(currentPos)
local h = (math.abs(v.x - self._endPos.x) + math.abs(v.y - self._endPos.y)) * OneRoadValue
node:setText(g, h)
end
end
end
local pos = self:getMinFPosInOpenTable()
if not pos then
return
end
-- 带延迟调用
-- local function fun()
-- self:checkDiretion(pos)
-- end
-- performWithDelay(self, fun, 2)
-- 不带延迟调用
self:checkDiretion(pos)
end
return RoadCheck
A星.png
ε≡٩(๑>₃<)۶ 请大家多多评论一起讨论讨论