A *导航网格路径查找


15

因此,我一直在这个名为Greenfoot的框架中制作这款自上而下的2D Java游戏,并且我一直在为您要战斗的家伙开发AI。我希望他们能够现实地环游世界,因此我很快意识到,除其他事项外,我需要某种寻路。

我已经制作了两个A *原型。一个是基于网格的,然后我制作了一个与航路点一起使用的栅格,因此现在我需要找到一种方法,从障碍物/建筑物的二维“图”获取可以作为路径的节点图。实际的寻路似乎很好,只是我的打开和关闭列表可以使用更有效的数据结构,但是如果需要的话,我会解决的。

我打算出于所有原因在ai-blog.net上使用导航网格。但是,我面临的问题是,A *认为从多边形中心/边开始的最短路径不一定是通过节点的任何部分的最短路径。为了获得更好的主意,您可以查看我在stackoverflow上提出问题

关于可见度图,我得到了很好的答案。从那以后,我已经购买了《计算几何:算法和应用程序》这本书并进一步阅读了该主题,但是我仍然偏爱导航网格物体(请参阅Amit关于路径查找的注释中的管理复杂性 ” )。(作为附带说明,如果第一个和最后一个不被遮挡,也许我可以使用Theta *将多个航路点转换为一条直线。或者每次我移回最后一个航路点之前,查看是否可以从这个)

因此,基本上我想要的是一个导航网格物体,一旦将其通过漏斗算法(例如,来自Digesting Duck的那个),我将获得真正的最短路径,而不是仅是从节点到节点的最短路径,但不是实际的最短距离,因为您可以穿过一些多边形并跳过节点/边。

哦,我也想知道您建议如何存储有关多边形的信息。对于航路点原型示例,我只是将每个节点作为一个对象,并存储了可以从该节点访问的所有其他节点的列表,我想这对多边形不起作用吗?以及如何判断多边形是可打开/可穿越的还是实心的?如何存储组成多边形的节点?

最后,作为记录:即使确实已有其他解决方案,我也希望自己从头开始编写此程序,并且我不打算在此游戏以外的任何其他程序中使用此代码,所以这无关紧要它将不可避免地是劣质的。


我不知道,但这些链接可以帮助:gamedev.stackexchange.com/questions/1327/... gamedev.stackexchange.com/questions/8087/...也有关于寻路,我无法找到另一个问题,现在,该赏金得到了很好的回答。
Ali1S232

是的,在第二个链接中,您可以看到我最关心的问题,A *算法将使用边缘中点将底部附近的路径设为最短,但障碍物顶部附近的路径实际上是最短的。我想知道如何获得A *来给我绕过顶部的路径,然后我将对其进行拉直(例如通过漏斗算法)以获得真正的最短路径,就好像它给了我一个围绕底部的路径一样,然后即使我把它拉直,它仍然绕道而行。
Angrydust

实际上,我只是阅读了gamedev.stackexchange.com/questions/8087/…中的文章,它似乎可以通过以下方法起作用:找到带有A *的路线,然后使用经过修改的漏斗算法计算出其真实成本,然后找到另一条路线并对其进行计算再次计算真实成本,看看它是否比另一个更短。重复进行直到知道找到最短的路线。这确实确实解决了我的问题,但是,由于重复进行拉直和路径查找,这似乎会很慢,这会非常昂贵。
Angrydust

多边形存储:仅存储可见的多边形-或将标签与每个多边形相关联(请记住,每个多边形将需要是顶点的圆形列表);与节点类似,您可以存储其起源的多边形的ID-但我不需要告诉您:它是基本数据存储。最后,为什么还要关心真正的最短路径?您的游戏运行缓慢,或者您的路径可能略有错误:选择一种。获得真正的最短路径需要完整的广度优先搜索(至少在节点图上)。
乔纳森·狄金森

@JonathanDickinson我知道我不需要到最后一个像素100%准确的路径,而且我知道我可以使用A *生成最多为p可接受的路径。事情进展得如此之显然是一个障碍,就像我之前关于堆栈溢出gamedev.stackexchange.com/questions/8087/…的问题所说的那样。我不能让我的AI绕过障碍!
Angrydust 2011年

Answers:


14

我建议即使您要编写所有自己的代码,也应下载Recast并构建示例应用程序,因为它具有显示生成的导航网格的可视化效果,并允许您通过简单的点来测试寻路并单击接口。您可以通过玩游戏学到很多东西。

正如您已经意识到的那样,要生成一个美观的路径,需要执行两个步骤-A *路径查找器,然后进行后续的后处理,其中包括路径拉直(消除了避免障碍物不必要的任何转弯)以及可能的在转折点处添加曲线。

寻找路径

您已经掌握了A *部分,这很棒。您还观察到A *不会总是找到最直的路径。至关重要的是要了解这是因为A *是一种算法,当将其应用于网格时,它是通过图形找到最短路径的算法(这是一个数学概念,其中节点是无量纲的),因此必须以某种方式将节点映射到网格元素。

最明显的事情是从多边形中心点导航到中心点,并将成本基于此距离,但这存在一些问题。一种是它不会总是找到几何上最短的路径,第二种是,如果您尝试遵循您所计算出的路径,则会注意到从一个中心到下一个中​​心的直线可以穿过并不是路径的一部分(并且可能根本无法导航)。这不是在执行A *时花费图表遍历的糟糕方法,但显然不足以满足任何遍历目的。

下一个最简单的解决方案是通过网格从一个边缘到另一个边缘执行A *。如果您想像每个多边形每个边一个,而不是每个多边形一个节点,您会发现更容易思考。因此,您的路径从起点到最近的一条边,再到相邻多边形的边之内,然后再到同一多边形的下一条边,依此类推。这会更频繁地产生最短路径,并且如果您不想执行路径矫正步骤,还具有可遍历的优点。

路径矫正

Detour(与Recast一起使用的导航库)中使用的算法非常简单。您应该观察到,它只会在A *搜索过程中找到的多边形边界内拉直路径。因此,如果在障碍物周围找不到最紧的路径,则在运行该算法后也不会获得紧的路径。在实践中,重铸产生的导航网格往往只有一个多边形,您可以在导航扼流点(两个障碍物之间的最接近点)时通过该多边形,因此A *会始终生成与障碍物一样近的节点列表。可能。如果您使用图块作为导航网格,则不是这种情况,这种非常简单的算法会插入虚假的转弯。

Detour的路径拉直算法的复杂度不是O(n),因为当它确定需要插入转弯时,会将其插入到最后一次将漏斗向左拧紧的位置(反之亦然),然后然后从该点开始再次跟踪节点。

如果要拉直构成A *路线一部分的多边形之外的路径,事情会变得复杂得多。您将需要实现一个射线投射例程,该例程可以测试导航网格中的两个点是否可以互相看见(无论如何,您都应该具有此点,以便可以看到是否根本需要使用A *)。通过将origin-> target所形成的线段与包含原点的多边形的连接边相交,然后测试将您移入的多边形的连接边来进行此操作。如果与不相交的边相交(我称它们为边界边),则说明您遇到了障碍。

然后,只要漏斗算法确定需要插入转弯以查看其是否确实需要,您就可以执行此光线投射测试,但是我认为您必须在每个节点上继续执行该测试,直到您插入转弯(在您可以恢复到简单的渠道算法)。这将变得很昂贵,使路径伸直大约为O(n ^ 2)。

代表导航网格

您可以将网格表示为多边形类的数组。多边形类可以很简单,只要有一个顶点数组和每个边缘到相邻多边形的引用数组即可。当然,您可能会想到更紧凑的存储方式。由于一个顶点通常由多个多边形共享,因此通常有一个大的顶点数组,然后每个多边形将索引存储到该数组中是正常的。根据导航网格的特性,连接边缘的平均数量可能仅为边缘数量的50%或更少。在这种情况下,您可能要存储到另一个多边形和边索引的链接,而不是为每个边存储链接。另外,我建议您将多边形的索引存储在导航网格的多边形数组中,而不要使用类引用。


我刚进行了简短的阅读,但我知道你永远不应该使用距离的平方(或不平方根吧)为A *:theory.stanford.edu/~amitp/GameProgramming/...
angrydust

我并不真正在乎如何实际进行路径矫正atm,我所关心的是您所说的话:“您应注意,它只会使在A *搜索期间找到的多边形边界内的路径伸直这样,如果在障碍物周围找不到最紧的路径,则在运行该算法后也不会获得紧的路径。”
2011年

我想拥有一个导航网格,无论通过顶点/中点旅行的成本如何,A *总是会找到一旦拉直的路径最短的路径。我欣赏可以用可见性图完成此操作,但是我想使用导航网格,因为它还有很多其他好处,并且因为可见性图的复杂性可以非常迅速地增长:theory.stanford.edu/~amitp/GameProgramming / ...
Angrydust

@theguywholikeslinux您可以使用欧几里得距离sqrt(x*x + y*y)-但不能使用每节点更便宜x*x + y*y
乔纳森·迪金森

@JonathanDickinson我知道,因此是我的意思。
Angrydust 2011年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.