我正在尝试在我的3D网格中实现A*算法进行路径规划。我一直在按照教程操作,但是没有得到有效的路径。我已经逐步检查了代码以找出问题所在,但是我不知道如何解决问题。对于最基本的测试,我只使用了一个2D网格(它是3D的,但只有一个Z选项,因此基本上是2D)。
所以我们从0,0(橙色)开始,希望到达1,2(绿色)。首先计算橙色方块的两个选项,向北和向东,并得到2和1.414的距离,F值分别为3和2.414。它移动到东侧方块(0,1)。很好。但现在它计算了从0,1开始的两个开放方块,它们是1,1和0,2,它们的g值为2,h值(距离)为1,使它们的F值都为3。
由于它们的F值为3,而我们已经有一个F值为3的选项(起始点的1,0),这两个选项被忽略,尽管它们显然是最佳选项。
然后它继续向前移动,并切换到移动到1,0,然后再次计算1,1为3,2,0为4.236。虽然1,1的f值不大于我们当前的f值,但它被忽略了,我们向上移动到2,0。
2,0只能向右移动,所以它这样做了。
由于它们的F值为3,而我们已经有一个F值为3的选项(起始点的1,0),这两个选项被忽略,尽管它们显然是最佳选项。
然后它继续向前移动,并切换到移动到1,0,然后再次计算1,1为3,2,0为4.236。虽然1,1的f值不大于我们当前的f值,但它被忽略了,我们向上移动到2,0。
2,0只能向右移动,所以它这样做了。
由于2,2是无效的方块,2,1只能向下移动,但移动到1,1的f值为3被保存,因此再次被忽略,使得我们在0,0和1,2之间没有有效路径。我错过了什么吗?
这是我的路径循环的一小段代码。这里有一堆自定义结构体,在使用Unreal Engine中的TMap存储我的关闭列表,但我认为那对问题没有影响。以下是关于这些结构体的简要说明:
PCell
:保存单元格坐标PPair
:将单元格坐标保存为PCell和F值FVectorInt
:三维整数向量FPathCell
:保存父坐标以及f、g和h值。cellDetails
是一个FPathCell
的三维动态数组closedMap
是一个TMap,其中<key, value>
为<IntVector, bool>
另外,locationIsWalkable(FVectorInt, StepDirection)
仅仅是检查玩家是否可以从特定方向走到一个单元的代码。您可以忽略该部分。
std::set<PPair> openList;
PPair originPair = PPair();
originPair.cell = PCell(i, j, k);
originPair.f = 0.0;
openList.insert(originPair);
bool foundDestination = false;
FPathCell destPair;
FVectorInt destCell;
while (!openList.empty() && !foundDestination)
{
iterations++;
PPair p = *openList.begin();
//Remove vertex
openList.erase(openList.begin());
//Add vertex to closed list
i = p.cell.i;
j = p.cell.j;
k = p.cell.k;
closedMap.Remove(FIntVector(i, j, k));
closedMap.Add(FIntVector(i, j, k), true);
double gNew, hNew, fNew;
//Generate movement options
//Option 1: NORTH (+X)
//Process if valid movement
if (locationIsWalkable(FVectorInt(i + 1, j, k), StepDirection::North))
{
FVectorInt check = FVectorInt(i + 1, j, k);
//If this cell is the destination
if (check == destination)
{
foundDestination = true;
//Set the parent of the destination cell
cellDetails[check.x][check.y][check.z].parent_i = i;
cellDetails[check.x][check.y][check.z].parent_j = j;
cellDetails[check.x][check.y][check.z].parent_k = k;
destPair = cellDetails[check.x][check.y][check.z];
destCell = check;
break;
}
//Else if this cell is not in the closed list
else if (!closedMap.FindRef(FIntVector(check.x, check.y, check.z)))
{
gNew = cellDetails[i][j][k].g + 1;
hNew = calculateHValue(check, destination);
fNew = gNew + hNew;
if (cellDetails[check.x][check.y][check.z].f == FLT_MAX ||
cellDetails[check.x][check.y][check.z].f > fNew) {
PPair cellPair = PPair();
cellPair.cell = PCell(check.x, check.y, check.z);
cellPair.f = fNew;
openList.insert(cellPair);
cellDetails[check.x][check.y][check.z].f = fNew;
cellDetails[check.x][check.y][check.z].g = gNew;
cellDetails[check.x][check.y][check.z].h = hNew;
cellDetails[check.x][check.y][check.z].parent_i = i;
cellDetails[check.x][check.y][check.z].parent_j = j;
cellDetails[check.x][check.y][check.z].parent_k = k;
}
}
}
//11 other movement options
}
inline bool operator<(const PPair& lhs, const PPair& rhs)
{
return lhs.f < rhs.f;
}
这里有12种移动选项(北、南、东、西、上+北、下+北等),但它们基本上都使用相同的代码,只是将check
向量替换为适当的移动方式。