| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- local tremove = table.remove
- local tinsert = table.insert
- local INFINITY_COST = 0xFFFFFF
- local BIAS_VALUE = 14
- local LINE_VALUE = 10
- local PathNode = class("PathNode")
- function PathNode:ctor()
- self.x = 0
- self.y = 0
- self.t = "new"
- self.h = 0
- self.k = 0
- self.isObstacle = 0
- self.backpointer = nil
- end
- function PathNode:cost(node)
- if self.isObstacle == 1 or node.isObstacle == 1 then
- return INFINITY_COST
- end
- local line_value = ((self.x == node.x or self.y == node.y) and 10 or 14)
- return ((self.x-node.x)*(self.x-node.x) + (self.y-node.y)*(self.y-node.y)) + line_value
- end
- --
- local PathFindAStar = class('PathFindAStar')
- function PathFindAStar:ctor()
- self.mapNodes = {}
- self.openlist = {}
- self.mapData = nil
- end
- function PathFindAStar:cleanup()
- self.startPathNode = nil
- self.endPathNode = nil
- self.mapNodes = {}
- self.openlist = {}
- end
- function PathFindAStar:updateLimits(begX, begY, endX, endY)
- local mapData = global.sceneManager:GetMapData2DPtr()
- self.mapRows = mapData:getMapDataRows()
- self.mapCols = mapData:getMapDataCols()
- local disX = 24
- local disY = 16
- self.minX = math.min(math.max(begX - disX, 0), self.mapCols-1)
- self.maxX = math.min(math.max(begX + disX, 0), self.mapCols-1)
- self.minY = math.min(math.max(begY - disY, 0), self.mapRows-1)
- self.maxY = math.min(math.max(begY + disY, 0), self.mapRows-1)
- local limits = {}
- for x = self.minX, self.maxX do
- limits[x] = {}
- for y = self.minY, self.maxY do
- limits[x][y] = mapData:isObstacle(x, y)
- end
- end
- return limits
- end
- function PathFindAStar:getPathNode(x, y)
- if not self.mapData[x] or not self.mapData[x][y] then
- return nil
- end
- if self.mapNodes[x] and self.mapNodes[x][y] then
- return self.mapNodes[x][y]
- end
- local node = PathNode.new()
- node.x = x
- node.y = y
- node.h = 0
- node.k = 0
- node.t = "new"
- node.backpointer = nil
- node.isObstacle = self.mapData[x][y]
- if not self.mapNodes[x] then
- self.mapNodes[x] = {}
- end
- self.mapNodes[x][y] = node
- return node
- end
- function PathFindAStar:getNeighbors(node)
- local neighbors = {}
- local around =
- {
- { node.x + 1, node.y }, -- right
- { node.x, node.y + 1 }, -- bottom
- { node.x - 1, node.y }, -- left
- { node.x, node.y - 1 }, -- top
- { node.x + 1, node.y - 1 }, -- right up
- { node.x + 1, node.y + 1 }, -- right bottom
- { node.x - 1, node.y + 1 }, -- left bottom
- { node.x - 1, node.y - 1 }, -- left top
- }
- for i = 1, 8 do
- local x = around[i][1]
- local y = around[i][2]
-
- -- out of bounds
- if (x >= self.minX and x <= self.maxX and y >= self.minY and y <= self.maxY) and self.mapData[x][y] == 0 then
- local node = self:getPathNode(x, y)
- if node then
- tinsert(neighbors, node)
- end
- end
- end
- return neighbors
- end
- function PathFindAStar:getMinKNode()
- local node = nil
- local k = nil
- local index = nil
- for i, v in pairs(self.openlist) do
- if not node or v.k < node.k then
- node = v
- k = node.k
- index = i
- end
- end
- return node, k, index
- end
- function PathFindAStar:insert(node, h_new)
- if node.t == "new" then
- node.k = h_new
- node.h = h_new
- node.t = "open"
- tinsert(self.openlist, node)
- elseif node.t == "open" then
- node.k = math.min(node.k, h_new)
- elseif node.t == "closed" then
- node.k = math.min(node.h, h_new)
- node.h = h_new
- node.t = "open"
- tinsert(self.openlist, node)
- end
- return true
- end
- function PathFindAStar:process_state()
- if #self.openlist == 0 then
- return -1
- end
- local currentNode, currentK, index = self:getMinKNode()
- if currentK == currentNode.h then
- for _, neighbor in pairs(self:getNeighbors(currentNode)) do
- local cost = currentNode.h + currentNode:cost(neighbor)
- if (neighbor.t == "new") or (neighbor.backpointer == currentNode and neighbor.h ~= cost) or (neighbor.backpointer ~= currentNode and neighbor.h > cost) then
- neighbor.backpointer = currentNode
- self:insert(neighbor, cost)
- end
- end
- else
- for _, neighbor in pairs(self:getNeighbors(currentNode)) do
- local cost = neighbor.h + currentNode:cost(neighbor)
- if neighbor.h <= currentK and currentNode.h > cost then
- currentNode.backpointer = neighbor
- currentNode.h = cost
- end
- end
- end
- currentNode.t = "closed"
- table.remove(self.openlist, index)
- if #self.openlist == 0 then
- return -1
- end
- local currentNode, currentK, index = self:getMinKNode()
- return currentK, currentNode
- end
- function PathFindAStar:find_path(begX, begY, endX, endY)
- self:cleanup()
-
- local points = {}
- if true == global.PathFindController:IsImpass(cc.p(begX, begY)) then
- return points
- end
- if true == global.PathFindController:IsImpass(cc.p(endX, endY)) then
- return points
- end
- if false == global.PathFindController:CheckMoveAble(cc.p(endX, endY)) then
- return points
- end
- self.mapData = self:updateLimits(begX, begY, endX, endY)
- self.endPathNode = self:getPathNode(begX, begY)
- self.startPathNode = self:getPathNode(endX, endY)
- if nil == self.startPathNode or nil == self.endPathNode then
- return points
- end
- -- start
- self:insert(self.endPathNode, 0)
- while true do
- local ret = self:process_state()
- if ret == -1 then
- return points
- elseif self.startPathNode.t == "closed" then
- break
- end
- end
- -- points
- tinsert(points, {x=self.startPathNode.x, y=self.startPathNode.y})
- local node = self.startPathNode.backpointer
- while node ~= self.endPathNode do
- tinsert(points, {x=node.x, y=node.y})
- node = node.backpointer
- if nil == node then
- points = {}
- break
- end
- end
- return points
- end
- return PathFindAStar
|