Sure, here’s the translation: A*寻路问题的处理(Java)

huangapple go评论61阅读模式
英文:

A* Pathfinding problems Processing(Java)

问题

我虽然在跟随一系列教程学习编程但最终得到了下面这段代码来处理我试图制作的小游戏的寻路问题

对于短而直的路径它能够工作但对于复杂的路径在一个仅为 54 * 46 的网格中它会卡住并且 `closedSet.size()` 会增长到大于 70000则不行

请注意,`wall` 函数根据碰撞瓦片的高度来确定是否为真因此它可能在从一个点传递为真的情况下在从另一个点传递时为假这是问题吗

```java
import java.util.*;

class YourTranslation {
  // ... (原始代码中的导入和其他内容)

  // 翻译内容开始
  int heuristic(int x, int y, int x_, int y_) {
    int dstX = abs(x - x_);
    int dstY = abs(y - y_);
    if (dstX > dstY) {
      return 14 * dstY + 10 * (dstX - dstY);
    } else {
      return 14 * dstX + 10 * (dstY - dstX);
    }
  }

  boolean wall(int x, int y, int x_, int y_) {
    Tile tileS = getTile(x, y);
    Tile tileCurr = getTile(x_, y_);
    if (abs(tileS.altitude - tileCurr.altitude) > 1 || tileS.altitude < 1) {
      return true;
    } else {
      return false;
    }
  }

  ArrayList<PVector> findPath(int startx, int starty, int endx, int endy) {
    Queue<Spot> openSet = new PriorityQueue<Spot>(fComparator);
    ArrayList<Spot> closedSet = new ArrayList<Spot>();

    Spot start = new Spot(startx, starty);
    Spot end = new Spot(endx, endy);
    Spot current = start;

    openSet.add(start);

    while (!openSet.isEmpty()) {
      current = openSet.poll();
      closedSet.add(current);

      println(closedSet.size());

      if (current.x == end.x && current.y == end.y) {
        break;
      }

      ArrayList<Spot> successors = new ArrayList<Spot>();

      for (int i = 0; i < collidingTiles.size(); i++) {
        JSONObject difference = collidingTiles.getJSONObject(i);
        /*JSONArray,例如
        [{x: -1, y: -1},{x: 0, y: -1},...](不包括 {x:0, y:0})
        */

        int x_ = difference.getInt("x");
        int y_ = difference.getInt("y");
        int x = x_ + current.x;
        int y = y_ + current.y;

        if (x >= 0 && x <= map.columns && y >= 0 && y <= map.rows) {
          Spot s = new Spot(x, y);
          successors.add(s);
        }
      }

      for (Spot s : successors) {
        if (!closedSet.contains(s) && !wall(s.x, s.y, current.x, current.y)) {
          int tempG = current.g + heuristic(s.x, s.y, current.x, current.y);

          if (tempG < s.g || !openSet.contains(s)) {
            s.g = tempG;
            s.h = heuristic(s.x, s.y, end.x, end.y);
            s.f = s.g + s.h;
            s.parent = current;
            if (!openSet.contains(s)) {
              openSet.add(s);
            }
          }
        }
      }
      successors.clear();
    }

    ArrayList<PVector> path = new ArrayList<PVector>();
    Spot temp = current;
    PVector tile = new PVector(temp.x + 0.5, temp.y + 0.5);
    path.add(tile);
    while (temp.parent != null) {
      tile = new PVector(temp.parent.x + 0.5, temp.parent.y + 0.5);
      path.add(0, tile);
      temp = temp.parent;
    }
    return path;
  }

  class Spot {
    int x, y;
    int f, g, h = 0;
    Spot parent;

    Spot(int x_, int y_) {
      x = x_;
      y = y_;
    }
  }

  Comparator<Spot> fComparator = new Comparator<Spot>() {
    @Override
    int compare(Spot s1, Spot s2) {
      return s1.f - s2.f;
    }
  };
  // 翻译内容结束
}

如果有其他问题或需要进一步的帮助,请随时提问。

英文:

I'm quite new to programming though following a bunch of tutorials I've ended up with this code to deal with the pathfinding of a small game I'm trying to make.

If works for small and straight paths but not for complex routes (it freezes and closedSet.size() gets larger than 70000 in a grid that is only 54 * 46).

Note that wall is true depending on the height of the colliding tiles, so it may be true coming from a point but false coming from another. Is that the problem?

import java.util.*;
int heuristic(int x,int y,int x_,int y_){
int dstX = abs(x - x_);
int dstY = abs(y - y_);
if(dstX &gt; dstY){
return 14*dstY + 10*(dstX - dstY);
}else{
return 14*dstX + 10*(dstY - dstX);
}
}
boolean wall(int x, int y, int x_, int y_){
Tile tileS = getTile(x, y);
Tile tileCurr = getTile(x_, y_);
if(abs(tileS.altitude - tileCurr.altitude) &gt; 1 || tileS.altitude  &lt; 1){
return true;
}else{
return false;
}
}
ArrayList&lt;PVector&gt; findPath(int startx, int starty, int endx, int endy){
Queue&lt;Spot&gt; openSet = new PriorityQueue&lt;Spot&gt;(fComparator);
ArrayList&lt;Spot&gt; closedSet = new ArrayList&lt;Spot&gt;();
Spot start = new Spot(startx, starty);
Spot end = new Spot(endx, endy);
Spot current = start;
openSet.add(start);
while(!openSet.isEmpty()){
current = openSet.poll();
closedSet.add(current);
println(closedSet.size());
if (current.x == end.x &amp;&amp; current.y == end.y) {
break;
}
ArrayList&lt;Spot&gt; successors = new ArrayList&lt;Spot&gt;();
for(int i = 0; i &lt; collidingTiles.size(); i++){
JSONObject difference = collidingTiles.getJSONObject(i);
/*JSONArray such as
[{x: -1, y: -1},{x: 0, y: -1},...](not including {x:0, y:0})
*/
int x_ = difference.getInt(&quot;x&quot;);
int y_ = difference.getInt(&quot;y&quot;);
int x = x_ + current.x;
int y = y_ + current.y;
if(x &gt;= 0 &amp;&amp; x &lt;= map.columns &amp;&amp; y &gt;= 0 &amp;&amp; y &lt;= map.rows){
Spot s = new Spot(x, y);
successors.add(s);
}
}
for(Spot s: successors){
if (!closedSet.contains(s) &amp;&amp; !wall(s.x, s.y, current.x, current.y)) {
int tempG = current.g  + heuristic(s.x, s.y, current.x, current.y);
if(tempG &lt; s.g || !openSet.contains(s)){
s.g = tempG;
s.h = heuristic(s.x, s.y, end.x, end.y);
s.f = s.g + s.h;
s.parent = current;
if (!openSet.contains(s)) {
openSet.add(s);
}
}
}
}
successors.clear();
}
ArrayList&lt;PVector&gt; path = new ArrayList&lt;PVector&gt;();
Spot temp = current;
PVector tile = new PVector(temp.x + 0.5, temp.y + 0.5);
path.add(tile);
while (temp.parent != null) {
tile = new PVector(temp.parent.x + 0.5, temp.parent.y + 0.5);
path.add(0, tile);
temp = temp.parent;
}
return path;
}
class Spot{
int x, y;
int f, g, h = 0;
Spot parent;
Spot(int x_, int y_){
x = x_;
y = y_;
}
}
Comparator&lt;Spot&gt; fComparator = new Comparator&lt;Spot&gt;() {
@Override
int compare(Spot s1, Spot s2) {
return s1.f - s2.f;
}
};

Any recommendations or minor changes are also appreciated.

答案1

得分: 3

> closedSet.size()在一个仅为54 * 46的网格中变大到超过70000

您的代码实际上实现了一些逻辑,即

  1. "如果一个节点已关闭,则不要再处理它",以及
  2. "如果节点已经在开放集中,则比较G值"

但在这两种情况下,它都不起作用,因为Spot未实现equals,因此contains在比较引用相等性,并且它将始终为false。因此,请实现Spot.equals。具体而言,使其仅比较xy,因为对于在此目的下被视为相等的节点,f/g/h/parent允许是不同的。

即使它起作用,对ArrayListPriorityQueue使用contains对性能也不太好。对于关闭列表,可以很容易地使用HashSet(当然,还要实现Spot.hashCode,以某种仅取决于xy的方式)。对于开放列表,摆脱缓慢的contains需要更多的工作。您可以使用的一个技巧是手动维护二叉堆,并且还可以有一个HashMap,它将x,y对映射到堆中对应节点所在的索引。手动维护堆的原因是必须在节点在队列中移动时更新HashMap,而普通的PriorityQueue没有这样的功能。

从性能角度来看,我对查找后继的方式也感到担忧,但我看不到详细信息。

> 请注意,wall根据碰撞瓦片的高度而定,因此它可能从一个点出发为真,但从另一个点出发为假。这是问题吗?

没关系,A*算法可以容忍一个位置从一侧可达,而从另一侧不可达。它无法本地考虑的是,如果达到一个位置的方向影响该节点具有的后继节点,但在这里不会发生。

英文:

> closedSet.size() gets larger than 70000 in a grid that is only 54 * 46

Your code does implement some logic that says

  1. "if a node is closed, don't process it again", and
  2. "if the node is already in the open set, compare G scores"

But in both cases it does not work, because Spot does not implement equals, and therefore contains is comparing for reference equality and it will always be false. So, implement Spot.equals. Specifically, make it compare only x and y, because f/g/h/parent are allowed to be different for nodes that are considered equal for this purpose.

Even when it works, using contains on an ArrayList and a PriorityQueue is not so good for performance. For the closed list, it is easy to use a HashSet (of course, also implement Spot.hashCode, in some way that depends only on x and y). For the open list, getting rid of slow contains is more work. One trick you can use is manually maintaining a binary heap, and additionally have a HashMap which maps an x,y pair to the index in the heap where the corresponding node is located. The reason for manually maintaining a heap is that the HashMap must be updated whenever nodes are moved in the queue, and the normal PriorityQueue does not have such functionality.

The way that finding successors works also worries me from a performance perspective, but I cannot see the details.

> Note that wall is true depending on the height of the colliding tiles, so it may be true coming from a point but false coming from another. Is that the problem?

That's fine, A* can tolerate a spot being reachable from one side but not an other. What it cannot natively take into account is if the direction a spot was reached from affects which successors that node has, but that does not happen here.

huangapple
  • 本文由 发表于 2020年8月19日 20:55:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/63487449.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定