在项目中遇到了自动寻路的需求,于是决定开始学习一下A星,对于A星我也没有深究,只能说是勉强搞定了需求,在这和大家分享一下,相互进步,
A星有个公式 f(x) = g(x) + h(x) ,搞清楚这个公式就好办了,f(x)就是当前位置到下一个位置的总价值,g(x)表示实际价,这是说这一部分代价是确定的,h(x)表示估价值,就是说我从下一个位置到到终点的代价是未知的,所以叫估价值,如图中所示,黑色格子表示当前位置,绿色格子表示下一步可能到达的位置,即上、下、左、右这几个方向,红色格子表示终点,褐色表示障碍物,现在要从黑色格子到达红色格子,那么黑色格子的下一步肯定是绿色格子当中的一个,黑色格子到绿色格子之间是相挨着的,所以我们可以很明确的知道它的实际代价为1(移动一步的代价)即g(x),绿色格子到红色格子之间隔着很长的距离,中间还有障碍物,所以这个代价是未知的,即h(x),所以总的代价就为f(x)
= g(x) + h(x),我们看到周围有4个绿色的格子,到底走那一步比较好呢,所以我们要把这4个格子的f(x)值都求出来,然后进行排序,选择f(x)值最小的,即总代价最少的那个格子,以此方法继续下去,直到到达终点 或者 地图上没有绿色格子了
下面来看一下这个工具类,g(x)和h(x)要选的比较合适,一般就是采用的曼哈顿算法,即两点在x方向和y方向的距离之和,
-- Filename: PathUtil.lua -- Author: bzx -- Date: 2014-07-01 -- Purpose: 寻路 module("PathUtil", package.seeall) local _map_data -- 地图数据 local _open_list -- 开放节点 local _open_map -- 开放节点,为了提高性能而加 local _close_map -- 关闭节点 local _deleget -- 代理 local _dest_point -- 目标点 local _start_point -- 起点 local _path -- 路径 -- 寻找路径 --[[ deleget = { g = function(point1, point2) -- add your code -- 返回点point1到点point2的实际代价 end h = function(point1, point2) -- add your code -- 返回点point1到点point2的估算代价 end getValue = function(j, i) -- 返回地图中第i行,第j列的数据 1为障碍物,0为非障碍物 end width -- 地图宽度 height -- 地图高度 } --]] function findPath(deleget, start_point, dest_point) _deleget = deleget _dest_point = dest_point _start_point = start_point init() while not table.isEmpty(_open_list) do local cur_point = _open_list[1] table.remove(_open_list, 1) _open_map[cur_point.key] = nil if isEqual(cur_point, dest_point) then return makePath(cur_point) else _close_map[cur_point.key] = cur_point local next_points = getNextPoints(cur_point) for i = 1, #next_points do local next_point = next_points[i] if _open_map[next_point.key] == nil and _close_map[next_point.key] == nil and isObstacle(next_point) == false then _open_map[next_point.key] = next_point table.insert(_open_list, next_point) end end table.sort(_open_list, compareF) end end return nil end function init() _open_list = {} _open_map = {} _close_map = {} _path = {} _map_data = {} for i = 1, _deleget.height do _map_data[i] = {} for j = 1, _deleget.width do local value = _deleget.getValue(j, i) _map_data[i][j] = value end end _open_map[getKey(_start_point)] = _start_point table.insert(_open_list, _start_point) end function createPoint(x, y) local point = { ["x"] = x, ["y"] = y, ["last"] = nil, ["g_value"] = 0, ["h_value"] = 0, ["f_value"] = 0 } point["key"] = getKey(point) return point end -- 得到下一个可以移动的点 -- @param point 当前所在点 function getNextPoints(point) local next_points = {} for i = 1, #_deleget.directions do local offset = _deleget.directions[i] local next_point = createPoint(point.x + offset[1], point.y + offset[2]) next_point["last"] = point if next_point.x >= 1 and next_point.x <= _deleget.width and next_point.y >= 1 and next_point.y <= _deleget.height then next_point["g_value"] = _deleget.g(point, next_point) next_point["h_value"] = _deleget.h(point, _dest_point)--math.abs(next_points.x - _dest_point.x) + math.abs(next_points.y - _dest_point.y) next_point["f_value"] = next_point.g_value + next_point.h_value table.insert(next_points, next_point) end end return next_points end -- 得到路径 -- @param end_point 目标点 function makePath(end_point) _path = {} local point = end_point while point.last ~= nil do table.insert(_path, createPoint(point.x, point.y)) point = point.last end local start_point = point table.insert(_path, start_point) return _path end -- 两个点的代价比较器 function compareF(point1, point2) return point1.f_value < point2.f_value end -- 是否是障碍物 function isObstacle(point) local value = _map_data[point.y][point.x] if value == 1 then return true end return false end -- 两个点是否是同一个点 function isEqual(point1, point2) return point1.key == point2.key end -- 根据点得到点的key function getKey(point) local key = string.format("%d,%d", point.x, point.y) return key end
下面是工具类PathUtil的用法
local deleget = {} deleget.g = function(point1, point2) return math.abs(point1.x - point2.x) + math.abs(point1.y - point2.y) end deleget.h = deleget.g deleget.getValue = function(j, i) local index = FindTreasureUtil.getIndex(j, i) local map_info = _map_info.map[index] if map_info.display == 0 and map_info.eid ~= 1 then return 0 end return 1 end deleget.directions = {{-1, 0}, {0, -1}, {0, 1}, {1, 0}} -- 左,上,下,右 deleget.width = _cols deleget.height = _rows local dest_row, dest_col = FindTreasureUtil.getMapPosition(tag) local dest_point = PathUtil.createPoint(dest_col, dest_row) local start_row, start_col = FindTreasureUtil.getMapPosition(_player_index) local start_point = PathUtil.createPoint(start_col, start_row) _path = PathUtil.findPath(deleget, start_point, dest_point)
_path就是我们找到的路径,起点为最后一个元素,终点为第一个元素,由于项目中的地图比较简单,因此我也没有过于深究,关于A星的介绍网上的资料还有很多,我这一般就显得太简略了,初次接触自动寻路,希望大神们多多指教
另附广告一条:出售美容品的微店 如需详细了解可加我姐姐QQ:937893128 各路大神 给你的老婆,女朋友,小三,小四,小五......还有你的众多基友买点吧