【问题标题】:Android path seeking algorithmAndroid寻路算法
【发布时间】:2016-05-08 15:44:14
【问题描述】:

上下文

我正在尝试开发一个简单的 2D 游戏,其中一些“僵尸”将追赶我。

我计算路径的想法如下(X = 路径不可用):

[4] [4] [X] [1] [1] [2] [3] [4] [5]
[3] [X] [X] [0] [1] [X] [X] [X] [5]
[3] [2] [1] [1] [1] [X] [3] [4] [5]
[3] [2] [2] [2] [2] [2] [3] [4] [5]

从 0 开始,给它周围的位置 1 值,到接近 1 的位置,给 2 值,等等。这样我只需要搜索一个较低的索引就知道到达 0 的最快方法。

问题

(1) 我不知道这个算法是否有名字,所以我找不到关于它的信息。

(2) 计算这个的最优解/算法/流程

(3) 在我的手机中,游戏屏幕的分辨率为 1700 x 1440,因此我的代码需要 15 秒。我创建了一个最终值来缩小所有内容并降低矩阵大小,但是,仍然需要很多,实际上无法播放。

(4) 还有其他需求吗?也许添加线程?我不知道这是否可行...

我的代码(调试代码已删除)

代码

private void expandAllFrom(int x, int y){ // x and y already scalled down
    nodes = new ArrayList<Point>(); // "nodes" is a global variable //
    nodes.add(new Point(x, y));

    while ( nodes.size() > 0 ){
        Point p = nodes.remove(0);
        expand(p.x, p.y);
    }
}

private void expand(int x, int y){
    int limXMin = x - 1, limXMax = x + 1, limYMin = y - 1, limYMax = y + 1;
    int value = map[x][y];

    // Check limits of screen
    if ( limXMin < 0 ) limXMin = 0;
    if ( limXMax > SCREEN_X_DIV - 1) limXMax = SCREEN_X_DIV - 1;

    if ( limYMin < 0 ) limYMin = 0;
    if ( limYMax > SCREEN_Y_DIV - 1) limYMax = SCREEN_Y_DIV - 1;

    for (int i = limXMin; i <= limXMax; i++){
        for (int j = limYMin; j <= limYMax; j++){
            if ( map[i][j] == 0 ) {
                if ( i != x || j != y ){
                    nodes.add(new Point(i, j));
                    map[i][j] = value + 1;
                }
            }
        }
    }
}

说明

我使用 FIFO 列表。我在其中添加节点,例如,流程将类似于:

(1) Add 0 position to expand node list.
(2) Expand 0 by setting 1 values arround it. Then add them to expand node list.
(2) Expand 1 by setting 2 values arround it. Then add them to expand node list.
(...)
(X) Expand 2 by setting 3 values arround it. Then add them to expand node list.
(Y) Expand 3 by setting 4 values arround it. Then add them to expand node list.
(...)

【问题讨论】:

  • 听起来像 A*(A-start)
  • 你的地图是随机的吗?
  • 顺便说一句,Queue 通常更适合 FIFO 列表。例如,LinkedList
  • 是的,但是它的实现方式,当你删除第一个元素时,所有元素都必须移动,LinkedList 没有这个问题。
  • 您不是在做 A*,从技术上讲,您正在对地图上的每个点进行 Dijkstra 算法(也称为 BFS,广度优先搜索)。你有多少僵尸?如果不是太多,您最好使用一种算法,该算法使用一些启发式 A* 类型搜索来搜索每个僵尸。看看qiao.github.io/PathFinding.js/visual 看看这些是如何工作的。

标签: java android algorithm artificial-intelligence


【解决方案1】:

这只是breadth-first search (BFS),用于查找单源最短路径。您要计算的数字与每个网格单元所在的 level 完全对应。好消息是,通过正确实施 BFS,您不需要这些数字。只需在玩家所在位置启动 BFS 程序,然后让每个僵尸走向它们当前所在单元格的 parent-pointer

【讨论】:

  • 哇,好方法。这一定比我的代码更有效率。这就是我打算这样做的方式。首先,创建矩阵并在其上添加僵尸位置。然后,开始扩展,当一个位置被僵尸占据时,将其移动到我之前的位置。那是正确的吗?有什么建议吗?
  • @UDKOX:你可以那样做,或者你可以做一个网格,做 BFS,然后每个僵尸在网格中查找它的位置并跟随父指针。
  • 这是我的第一个想法。但是,在我只有几个丧尸的情况下,计算所有级别,是一种资源浪费。所以,我想我会试试我上面说的,但谁知道呢,我还是要阅读其他的 cmets。
  • @UDKOX:我要问自己的第一个问题是:每个像素需要一个网格单元吗?这似乎比简单搜索行为所需的精度要高得多。
  • @UDKOX:性能将取决于具体情况。如果您有非常多的僵尸和/或许多被阻挡的位置,那么计算单个路径的算法将会受到影响。 BFS 的好处是它总是花费相同的时间,无论僵尸的数量/位置或玩家的位置如何。缺点是 BFS 在大型开放环境中无法很好地扩展,因为僵尸很少。
【解决方案2】:

如前所述,您所做的称为breadth first search,它是Dijkstra's algorithm 的一个特例。为自己找到它做得很好!

问题是,BFS 的时间复杂度是O(V+E),其中V 是节点数,E 是边数。在您的情况下,它将按地图大小的顺序排列,具体取决于地图的稀疏性(即有多少个 X-es)。对于大小为 1700x1440 的地图而言,这无论如何是数百万的数量级。

如果僵尸的数量不太大,使用 BFS 的变体,一个一个地计算每个僵尸的最短路径会快得多(您仍然可以在僵尸之间共享和重用扩展的节点)启发式。例如,jump point search 针对统一成本迷宫进行了优化(跳转点搜索是A-star algorithm 的一个特例)。

这里的想法是取一个起点(僵尸的位置)和一个终点(玩家的位置),并计算它们之间的最短路径,首先扩展离目的地更近的节点。这里更近意味着到端点的近似距离更小。到达节点的距离是已知的,A 星将选择从起点到节点的距离之和加上从节点到终点的近似距离最小的节点。由于您允许对角线移动,因此距离近似值不能是Manhattan distance,也不能是欧几里德距离,因为近似值必须是实际距离的下限。你可以拿例如。最大值(│x-x'│,│y-y'│)。跳转点搜索通过利用地图的迷宫结构进一步改进了这一点,以排除更多节点。

This site 动画了几个这样的算法,所以你可以感受一下它们是如何工作的。

这种方法的好处是你不会搜索整个地图,只搜索僵尸和玩家之间的一小部分。这可能已经比任何全面的 BFS 算法快几个数量级。为了展示加速的戏剧性,请看下面的图片。搜索只探索标记的节点:

另一个优点是,你可以在运行时间和僵尸的“聪明”之间做出妥协。您所要做的就是不要将这样的算法一直运行到终点。您可以在预定义的步数后停止并获得路径开始的近似值(通过查看起点和最有希望的节点之间的最短路径来扩展下一步)。因此,根据您有多少时间进行计算,您可能有最佳或不太理想的僵尸。

【讨论】:

  • 如果我错了,请纠正我。 A* 试图扩展曼哈顿距离最短的那个? (曼哈顿距离 = ( |X1-X0| + |Y1-Y0| ))。使用欧几里得距离会有什么影响吗(除了微积分的时间)?
  • 到达节点的距离是已知的。 A* 扩展到达的节点之一,其中(到开始的距离)+(到结束节点的近似距离)是最小的。我编辑了我的答案,因为我意识到您允许对角线步进,因此您不能严格使用曼哈顿距离,因为近似值必须是距离的下限才能使 A* 起作用。从这个意义上说,欧几里德距离也不起作用。可行的是 max(│x-x'│,│y-y'│) ,它可以在空地图上为您提供最少的步数。
  • 你知道我可以使用什么简单的实现吗?
  • 我不知道有什么可以开箱即用的。这很困难,因为每个项目都有自己的世界代表。您可以适应许多实现,例如。这个:github.com/ClintFMullins/JumpPointSearch-Java
  • JPS 在网格设置中总是会更好。 A* 更通用,可以应用于更广泛的问题。
猜你喜欢
  • 1970-01-01
  • 2011-02-05
  • 2015-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-08-20
  • 2017-03-02
相关资源
最近更新 更多