‘壹’ A星寻路算法和Unity自带的寻路相比有什么优势
并没一种寻路适合所有场合,选择都是基于需求而定的。
1. A* 算法与贪婪算法不一样,贪婪算法适合动态规划,寻找局部最优解,不保证最优解。
A*是静态网格中求解最短路最有效的方法。也是耗时的算法,不宜寻路频繁的场合。一般来说适合需求精确的场合。
与启发式的搜索一样,能够根据改变网格密度、网格耗散来进行调整精确度。
使用的地方:
a. 策略游戏的策略搜索
b. 方块格子游戏中的格子寻路
2. Unity 自带的导航网格系统
Unity 内置了NavMesh导航网格系统,一般来说导航网格算法大多是“拐角点算法”。
效率是比较高的,但是不保证最优解算法。
使用的地方:
a.游戏场景的怪物寻路
b.动态规避障碍
‘贰’ A星寻路算法和Unity自带的寻路相比有什么优势
在理解Navigation的时候,首先要明确两个知识点:
AStar:AStar是路点寻路算法中的一种,同时AStar不属于贪婪算法,贪婪算法适合动态规划,寻找局部最优解,不保证最优解。AStar是静态网格中求解最短路最有效的方法。也是耗时的算法,不宜寻路频繁的场合。一般来说适合需求精确的场合。
性能和内存占用率都还行,和启发式的搜索一样,能够根据改变网格密度、网格耗散来进行调整精确度。
A Star一般使用场景:
策略游戏的策略搜索
方块格子游戏中的格子寻路
Navigation:网格寻路算法,严格意义上它属于”拐角点算法”,效率是比较高的,但是不保证最优解算法。Navigation相对来说消耗内存更大,性能的话还不错。
Navigation一般使用场景:
游戏场景的怪物寻路
动态规避障碍
它们二者事件的实现方式和原理都不同。
AStar的话,
‘叁’ 算法过程是什么
‘肆’ 什么是A搜索算法
A*搜索算法,俗称A星算法,作为启发式搜索算法中的一种,这是一种在图形平面上,有多个节点的路径,求出最低通过成本的算法。常用于游戏中的NPC的移动计算,或线上游戏的BOT的移动计算上。该算法像Dijkstra算法一样,可以找到一条最短路径;也像BFS一样,进行启发式的搜索。
‘伍’ A*算法用于路径规划,有什么缺点
缺点:A*算法通过比较当前路径栅格的8个邻居的启发式函数值F来逐步确定下一个路径栅格,当存在多个最小值时A*算法不能保证搜索的路径最优。
A*算法;A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法。估价值与实际值越接近,估价函数取得就越好。A*[1] (A-Star)算法是一种静态路网中求解最短路最有效的直接搜索方法。注意是最有效的直接搜索算法。之后涌现了很多预处理算法(ALT,CH,HL等等),在线查询效率是A*算法的数千甚至上万倍。公式表示为: f(n)=g(n)+h(n),其中 f(n) 是从初始点经由节点n到目标点的估价函数,g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n) 是从n到目标节点最佳路径的估计代价。保证找到最短路径(最优解的)条件,关键在于估价函数f(n)的选取:估价值h(n)<= n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。但能得到最优解。并且如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行, 此时的搜索效率是最高的。如果 估价值>实际值,搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
‘陆’ A星搜索算法
A星算法是定义了一个函数f,公式为:
f = g + h
其中g函数代表目前为止从出发地到达该节点的成本,h函数是预估的当前节点到到目的地的成本,即
g(path) = path cost
h(path) = h(s) = estimated distance to goal
朝着使函数f具有最小值的路径拓展,该算法可以找到消耗最小消耗的路径
注意A星算法并不是总能找到最优解,能否找到最优解依赖于h函数,条件是
‘柒’ lua语言a星寻路算法路径怎么平滑
在项目中遇到了自动寻路的需求,于是决定开始学习一下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: 寻路
mole("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*算法原理
A 算法是启发式算法重要的一种,主要是用于在两点之间选择一个最优路径,而A 的实现也是通过一个估值函数
上图中这个熊到树叶的 曼哈顿距离 就是蓝色线所表示的距离,这其中不考虑障碍物,假如上图每一个方格长度为1,那么此时的熊的曼哈顿距离就为9.
起点(X1,Y1),终点(X2,Y2),H=|X2-X1|+|Y2-Y1|
我们也可以通过几何坐标点来算出曼哈顿距离,还是以上图为例,左下角为(0,0)点,熊的位置为(1,4),树叶的位置为(7,1),那么H=|7-1|+|1-4|=9。
还是以上图为例,比如刚开始熊位置我们会加入到CLOSE列表中,而熊四周它可以移动到的点位我们会加入到OPEN列表中,并对熊四周的8个节点进行F=G+H这样的估值运算,然后在这8个节点中选中一个F值为最小的节点,然后把再把这个节点从OPEN列表中删除,加入到Close列表中,从接着在对这个节点的四周8个节点进行一个估值运算,再接着依次运算,这样说大家可能不是太理解,我会在下边做详细解释。
从起点到终点,我们通过A星算法来找出最优路径
我们把每一个方格的长度定义为1,那从起始点到5位置的代价就是1,到3的代价为1.41,定义好了我们接着看上图,接着运算
第一步我们会把起始点四周的点加入OPEN列表中然后进行一个估值运算,运算结果如上图,这其中大家看到一个小箭头都指向了起点,这个箭头就是指向父节点,而open列表的G值都是根据这个进行计算的,意思就是我从上一个父节点运行到此处时所需要的总代价,如果指向不一样可能G值就不一样,上图中我们经过计算发现1点F值是7.41是最小的,那我们就选中这个点,并把1点从OPEN列表中删除,加入到CLOSE列表中,但是我们在往下运算的时候发现1点的四周,2点,3点和起始点这三个要怎么处理,首先起始点已经加入到了CLOSE,他就不需要再进行这种运算,这就是CLOSE列表的作用,而2点和3点我们也可以对他进行运算,2点的运算,我们从1移动到2点的时候,他需要的代价也就是G值会变成2.41,而H值是不会变的F=2.41+7=9.41,这个值我们发现大于原来的的F值,那我们就不能对他进行改变(把父节点指向1,把F值改为9.41,因为我们一直追求的是F值最小化),3点也同理。
在对1点四周进行运算后整个OPEN列表中有两个点2点和3点的F值都是7.41,此时我们系统就可能随机选择一个点然后进行下一步运算,现在我们选中的是3点,然后对3点的四周进行运算,结果是四周的OPEN点位如果把父节点指向3点值时F值都比原来的大,所以不发生改变。我们在看整个OPEN列表中,也就2点的7.41值是最小的,那我们就选中2点接着运算。
我们在上一部运算中选中的是1点,上图没有把2点加入OPEN列表,因为有障碍物的阻挡从1点他移动不到2点,所以没有把2点加入到OPEN列表中,整个OPEN列表中3的F=8是最小的,我们就选中3,我们对3点四周进行运算是我们发现4点经过计算G=1+1=2,F=2+6=8所以此时4点要进行改变,F变为8并把箭头指向3点(就是把4点的父节点变为3),如下图
我们就按照这种方法一直进行运算,最后 的运算结果如下图
而我们通过目标点位根据箭头(父节点),一步一步向前寻找最后我们发现了一条指向起点的路径,这个就是我们所需要的最优路径。 如下图的白色选中区域
但是我们还要注意几点
最优路径有2个
这是我对A*算法的一些理解,有些地方可能有BUG,欢迎大家指出,共同学习。