對於2d地圖的a星算法其實很多。原理都是一樣的,我做這個的3d的尋路其實只是在2d地圖數據中加入了一個高度的數據,而在權重中由原來的兩個座標的勾股定理變爲三維座標系的勾股定理,以前我做cocos2dx開發時曾寫過一個lua版的a星,就用這個作爲說明吧(涉及a星的原理就不必再說明了,很多博客都會有):
其中的mapInfo可以更改爲如下
self.MapInfo =
{
[8] = {{"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255},
{"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height" = 255}, {"obstacle" = 0, "height"
= 255}, {"obstacle" = 0, "height" = 255}},
...
地圖的點也需要記錄一個高度值
local xSub = math.abs(x - endx)
function MapPoint:init(x, y, h)
self.parent = nil; --父節點,最後回溯使用
self.x = x; --x座標
self.y = y; --y座標
self.h = h;
--高度
self.cost = 0; --尋路到此點累計的消耗
self.dist = 0; --該點距離目的地直線距離(大約)
self.power = 0; --該點的累計消耗以及直線距離總和
end
再來看看具體尋路的代碼:
--[[
* @authors: liangjian
* @date: 2014-09-01 21:30:09
* @desc: a*算法
--]]
module( "Astar", package.seeall )
function new()
local obj = {}
setmetatable(obj, {__index = Astar})
obj:init()
return obj
end
function Astar:init()
self.openList = {}; --打開,表示可以進行尋路的節點列表
self.closeList = {}; --關閉,表示節點已經搜索過
self.mapData = {}; --地圖數據
self.width = 0; --地圖的寬
self.height = 0; --地圖的高
self.startX = 0; --開始點x
self.startY = 0; --開始點y
self.endX = 0; --終點x
self.endY = 0; --終點y
self.path = {}; --保存路徑
end
--設置地圖數據
function Astar:setMapData(mapData)
if mapData ~= nil and #mapData ~= 0 then
self.mapData = mapData;
self.width = #self.mapData[1];
self.height = #self.mapData;
end
end
--搜索,尋路前準備
function Astar:search(startX, startY, endX, endY)
self.startX = startX;
self.startY = startY;
self.endX = endX;
self.endY = endY;
--判斷是否超出地圖外
if (self.startX<0 or self.startX>=self.width) or (self.startY<0 or self.startY>=self.height) then
print("出發點不在地圖範圍內!");
return;
elseif (self.endX<0 or self.endX>=self.width) or (self.endY<0 or self.endY>=self.height) then
print("目的點不在地圖範圍內!");
return;
end
--判斷目的地以及出發地是否符合,1爲障礙物,0通行
if self.mapData[startY + 1][startX + 1] == 1 then
print("出發地爲障礙點!");
return;
end
if self.mapData[endY + 1][endX + 1] == 1 then
print("目的地爲障礙點!");
return;
end
--判斷出發地與目的地是否相同
if self.startX == self.endX and self.startY == self.endY then
print("目的地與出發地相同!");
return;
end
--根節點,第一個放進開放列表中
local root = MapPoint.new(startX, startY);
table.insert(self.openList, root);
--開始尋路
self:start(endX, endY);
if self.path==nil or #self.path==0 then
print("沒有找到路徑!");
else
print("輸出路徑:\n");
for i = 1, #self.path do
print("("..self.path[i].x..","..self.path[i].y..")\n");
end
end
end
--開始尋路,是個遞歸函數
function Astar:start(endX, endY)
if self.openList == nil or #self.openList == 0 then return 0; end
local least = self.openList[1];
if least.x == endX and least.y == endY then
self:recallGetPath(least)
print("找到路徑了!");
return self.path;
end
--八個方向檢查
self:check(least.x-1, least.y, 10, least, endX, endY) --左
self:check(least.x+1, least.y, 10, least, endX, endY) --右
self:check(least.x, least.y+1, 10, least, endX, endY) --上
self:check(least.x, least.y-1, 10, least, endX, endY) --下
self:check(least.x-1, least.y+1, 14, least, endX, endY) --左上
self:check(least.x+1, least.y+1, 14, least, endX, endY) --右上
self:check(least.x-1, least.y-1, 14, least, endX, endY) --左下
self:check(least.x+1, least.y-1, 14, least, endX, endY) --右下
--將當前節點放進關閉節點中
table.insert(self.closeList, least)
--將此節點從打開列表移除
table.remove(self.openList, 1)
self.openList = self:sortByPower(self.openList)
self:start(endX, endY)
end
--檢查是否通路
function Astar:check(x, y, cost, parent, endX, endY)
if (x<0 or x>=self.width) or (y<0 or y>=self.height) then
print("該點處於地圖外!");
return;
end
if self:isContain(x, y, self.closeList) ~= -1 then
print("該點已經訪問過!");
return;
end
if self.mapData[y + 1][x + 1] == 1 then
print("該點爲障礙物!");
return;
end
--累計到此點的消耗
local node = MapPoint.new(x, y);
node.parent = parent
node.cost = parent.cost + cost
--如果訪問到開放列表中的點,則將此點重置爲消耗最小的路徑,否則添加到開放列表
local index = self:isContain(x, y, self.openList)
if index ~= -1 then
if node.cost<self.openList[index].cost then
node.dist = self.openList[index].dist
node.power = node.dist + node.cost
table.remove(self.openList, index)
table.insert(self.openList, index, node)
end
else
node.dist = self:getDist(x, y, endX, endY)
node.power = node.dist + node.cost
table.insert(self.openList, node)
end
end
--計算點到目的地距離
function Astar:getDist(x, y, endx, endy)
local xSub = math.abs(x - endx)
local ySub = math.abs(y - endy)
return math.sqrt(xSub*xSub + ySub*ySub)
end
--開啓列表按照消耗排序
function Astar:sortByPower(list)
local sortList = {};
local size = #list
for i = 1, size do
local minIndex = 1
for j = 2, #list do
if list[minIndex].power > list[j].power then
minIndex = j;
end
end
table.insert(sortList, list[minIndex]);
table.remove(list, minIndex);
end
return sortList;
end
--判斷點是否包含,返回索引值
function Astar:isContain(x, y, list)
for i = 1, #list do
if x == list[i].x and y == list[i].y then
return i;
end
end
return -1;
end
--回溯找到路徑
function Astar:recallGetPath(node)
table.insert(self.path, 1, {x = node.x, y = node.y})
if node.parent ~= nil then
self:recallGetPath(node.parent)
end
end
--沒有找到路徑
function Astar:notFoundPath()
self.path = {}
end
需要修改getDist(x, y, endx, endy)函數,增加一個高度差值
function(x, y, h, endx, endy, endz)
local xSub = math.abs(x - endx)
local ySub = math.abs(y - endy)
local zSub = math.abs(z - endz)
return 勾股定理
end