【问题标题】:Pathfinding on large map大地图寻路
【发布时间】:2016-09-24 19:19:32
【问题描述】:

我正在创建一个具有 10,000 x 10,000 地图的游戏。
我希望用户能够设置位置并让计算机立即找到最佳路径。
但是,由于地图是 10,000 x 10,000,因此有 100,000,000 个节点,通过 A* 或 Dijkstra 等常规方法找到该路径需要大量内存和较长时间。
所以我的问题是:如何找到最佳路径?
我正在考虑的算法会将世界划分为 100 个部分,每个部分有 1,000,000 个节点。然后将每个部分分为 100 个子部分。这将重复,直到每个小节包含 100 个节点。然后,该算法将找到部分的最佳路径,然后是子部分,然后是子子部分,直到找到最佳节点集。这行得通吗?还有更好的方法吗?
我也在考虑跳点搜索,但我不知道,学习发现它做不到会很痛苦。

编辑:我已尝试添加 A*。但是,运行大约需要 5 秒,比理想情况多出大约 4 秒。

【问题讨论】:

  • 像使用我们的普通街道地图一样,用 10,000 x 10,000 的地图构建图表。我敢肯定,你最终得到的图表比这 100,000,000 小得多....
  • 您是否尝试过在这张地图上实现 A*?如果它不是一个迷宫,那么 A* 绝不应该真正扩展所有这 100m 个节点。我不明白为什么它不应该工作。
  • 为什么不使用网格图 A* 而不是图 A* ?是的,分辨率相当大,但是您可以做一些事情来真正加快速度,例如:仅扫描使用区域的边界框,从两端投射路径,屏蔽不可通过的封闭区域等......另外,如果你空间更开放,然后你可以忽略离直线路径太远的地图......见map A*它可以被编码,所以它不需要遍历点列表只是你已经在内存中拥有的地图(虽然不是二进制)
  • 您仍然可以提供更多信息。你的地图看起来怎么样?障碍物有多密集或稀疏?地图有多像迷宫?地图的大部分区域是否完全无法到达?所有这些都可能对算法的最佳选择产生影响。也许您可以提供一个示例作为图像(按比例缩小或只是摘录)。
  • 既然是游戏,是否真的有必要一直使用最短路径,还是可以放宽,这样走的路径可能比最短路径多出10%的成本路径?

标签: java algorithm path-finding a-star


【解决方案1】:
  1. 确保所有图形数据都在内存中
  2. 使用双向 Dijkstra - 假设您拥有多核
  3. 考虑使用收缩层次结构,这将大大提高性能。
  4. 尽可能预先计算所有内容,例如路径权重。

【讨论】:

    【解决方案2】:

    一个高级概念可能是找到起点和终点的点 - 比如说点 (0,0) 和点 (10000, 10000) - 并对从起点到终点的路径进行初步猜测(在此如果它会一直沿对角线向上和向右运行)然后开始检查它是​​否可以成功到达那里(如果该路径上有障碍物)。如果有然后以编程方式选择相似的路径,或者找到路径失败的地方并从那里开始并尝试迭代直到它工作,它可能不是 100% 最快的,但你会得到比找到每一种可能的方式更好的结果,然后从中推断出最短路径。

    实施

    • 寻找初始最短路径的方法
    • 运行看看它是否有效
      • 如果失败,则尝试类似的路径或从失败开始

    【讨论】:

      【解决方案3】:

      如果您的地图具有统一的权重(当然障碍物除外),您可能会在以下任一情况下获得更好的性能:

      1. 一种将网格预处理为图形的算法,将大的空白空间折叠成单个节点。导航网格将可遍历区域分解为凸多边形,每个凸多边形都可以一步遍历。 L1 path finder 将障碍物组合在一起,将它们简化为可见图,从而计算出路径。

      2. 一种不会扩展每个节点的算法。 Jump-point search 利用不同路径之间的对称性,仅扩展与障碍物相邻的节点,而 A* 将沿最短路径扩展每个节点。

      【讨论】:

        【解决方案4】:

        由于地图为 10.000 x 10.000,因此节点数为 100.000.000。使用 A* 的直接实现是不切实际的,而且肯定不会使游戏在地图大小上具有可扩展性。

        我建议您使用以下解决方案,这基本上是您的想法:

        HPA*(层次路径 A*)。此方法创建不同的地图层次结构。您可以通过说每个 100x100 像素块是一个区域来自动化该过程。然后,对于每个块,我们需要找到相邻的块以及每个块的入口和出口在哪里。 如果两个块之间的连接不止一个节点,那么我们使用两个节点来表示问题。这张图片解释了我们正在尝试构建的新图表。 (黑色=障碍物,灰色是块之间的连接节点)。

        此方法提供了良好的结果,从使用游戏 Baldur's Gate 中的地图执行中可以看出,每个块都是 10x10。

        有关更多信息,请阅读 Nathan Sturtevant 的这篇文章(他是游戏领域最成功的寻路研究者之一)。 https://skatgame.net/mburo/ps/path.pdf

        有关 HPA 的说明,请查看 Sturtevant 的这个讲座(HPA 至少 43:50)。 https://www.youtube.com/watch?v=BVd5f66U4Rw

        此外,如果您想了解 HPA* 的实际应用,请查看 Sturtevant 制作的以下视频: https://www.youtube.com/watch?v=l7YQ5_Nbifo

        【讨论】:

          【解决方案5】:

          因此,即使在正方形 n × n 地图上可能有 n^4 最短路径。存储所有路径不一定需要O(n^4) 空间。这个想法是,给定地图上的两个不同的目标位置和它们的最短路径树,这两个点在地图上越接近,它们的最短路径树的共同元素就越多。在使用平面地图(如带有障碍物的网格)时尤其如此。

          所以这个想法是只为一小组目标位置(甚至可能只有一个目标位置)存储一些完整的最短路径树。对于其余目标位置,仅存储其最短路径树与先前存储的最短路径树之一的差异。

          那么找到从一个位置到目标的最短路径的算法是加载一个完全存储的最短路径树,然后对其应用一些差异来获得目标位置的最短路径树。那么只需要在最短路径树上找到当前玩家位置,即O(n^2)complexity。

          对于存储最短路径树及其差异需要多少存储空间,我没有任何确凿的事实,但这可能在O(n^2 log(n^2)) 的范围内。加载一个并应用差异可能只有O(n^2) 的时间复杂度。

          目标位置的最短路径树表示从地图上每个位置到目标位置的所有最短路径。

          此解决方案还可以将使用过的最短路径树保留在内存中,并根据需要应用差异,以便使用新的最短路径树。那么获取最短路径树的复杂性不受地图大小的限制,而仅受要应用的差异大小的限制。这种情况可能真的适用于原始的《神圣》或《暗黑破坏神》等游戏。

          【讨论】:

          • 听起来是个不错的主意,尤其是如果您有一种快速找到“最佳”最短路径树的方法(这可能是“根”最接近任一起点的路径树点或目的地)。 OTOH,最短路径可能不止 O(n^4) 条...... - 左下角到右下角(即仅向下或向右移动的序列)。这个数字大约是 4^n / sqrt(pi*n)。
          【解决方案6】:

          这将比适合评论的内容长一点,因此是一个答案。

          您的设置需要澄清。 10,000x10,000 都很好,但这种说法不是:

          由于地图是 10,000 x 10,000,因此有 100,000,000 个节点

          为什么坐标系的每个单位都有 1 个节点?这不是节点寻路的工作方式,而是节点应该更稀疏,并且通过它们的存在来描述路径上的单个(稀疏)点。在节点之间,对象通过其他方式处理移动。 grid 寻路系统在最坏的情况下(如果没有障碍物的话)可能有 100,000,000 个点,但正如 Q 提到的 nodes,我认为这是关于节点寻路的。

          100,000,000 个节点

          如果是 int32,100,000,000 个节点是 381 MB 的内存,如果是 float64,是 763 mb。此外,还有节点连接。我不知道在您的情况下如何设置这些,但每个连接都需要 2 个整数,例如每个 2 个字节。 IE。如果有与节点一样多的连接,则需要另外 381 mb。总而言之,我们最终得到接近 1 TB 的图形数据,我声称那肯定有问题。

          如果我们仍然有一个巨大的节点图/一个巨大的区域,如何解决这个问题?我可能会通过创建更大的象限来简化(正如你提到的)。然而,每个象限将仅沿 4 条边保存节点 - 象限内的所有节点都将被直线替换。这样,就可以解决每个象限的进入/退出点。这将是一个单独的节点图,用于粗略计算。然后,在一个象限内,总是会仅在某个时间加载该象限的内部节点图。 Ofc 会涉及某种错误,但是,嘿,那是现实生活,对吧?如果这是关于人类行为的,那么它并不总是完全优化的。

          预计算、缓存、速度、小数据是游戏编码中的关键词。

          【讨论】:

          • 我认为您不会将路径保存为 int。它可以是可用的,也可以是不可用的。可以取也可以不取,所以基本上是1位。 100,000,000 -> 12.5 MB,因此它没有那么多内存。一旦你走到了一条路,你就把它标记为已走。
          【解决方案7】:

          我对问题陈述的初步理解如下。地图上有预定义的终端位置。用户在地图上选择一个位置,必须找到到最近的位置的最佳/最短路径。

          如果我的理解是正确的,那么您可以通过 BFS 算法的单个应用程序预先计算地图上所有位置的最短路径。您可以在每个节点仅使用 2 位有效地存储该信息(与每个节点关联的值将告诉您必须从该节点向哪个方向移动才能保持最短路径)。

          然而,正如tobias_k 所评论的那样,问题的定义可能不同——玩家选择地图上的任意位置,并且必须找到从当前位置到该位置的最佳路径。可以再次使用前面描述的方法,前提是

          1. 玩家在地图上移动的速度不会太快,并且
          2. 有些不准确是可以容忍的。

          然后执行已经描述的算法以找到从地图上的任何位置到以玩家当前位置为中心的小圆周的最短路径。然后在短时间内,该数据可用于快速将准最短路径路由到地图上的任何位置。当玩家在移动时太靠近该圆圈的边界时,算法会抢先执行玩家的新位置。

          这种方式的缺点是会消耗大量的 CPU 资源。优点是简单。

          【讨论】:

          • “我希望用户能够设置位置并让计算机立即找到最佳路径。”目标位置是在计算路径之前选择的,因此“预先计算路径”没有帮助。预先计算什么目标?
          • 我理解的问题陈述如下。地图上有预定义的终端位置。用户在地图上选择一个位置,必须找到到最近的位置的最佳/最短路径。
          • 好吧,我的理解是玩家在地图上的任意位置选择一个目标位置,算法应该从玩家当前位置找到一条路径到目标。
          • 感谢您的提示,这是有道理的。也会尝试解决它。​​
          • 我曾经遇到过类似的问题,我尝试将每个可能的下一个节点与目标节点进行比较,无论哪个最相似,都对那个做。诚然,这带来了一系列新问题,但值得考虑!
          猜你喜欢
          • 2022-11-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-04-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多