1

我正在尝试实现 Jump Point Search,这是对 A* 的优化,可将打开列表中的节点数量平均减少 20 倍,以及内存消耗。该算法是根据原始研究论文和一些在线教程实现的,但是程序在搜索自然邻居和强制邻居时进入了无限递归循环,导致堆栈溢出。寻路 A* 已经在工作,我只是在其中添加 JPS 函数。

以下是这些功能的代码:

void Movement::GetNaturalNeighbors(std::vector<Node>& output, Node& currentNode)
{
    int nextX = currentNode.pos.x + currentNode.direction.x;
    int nextY = currentNode.pos.y + currentNode.direction.y;

    // Straight line pruning excludes all neighbors except the one directly in front of currentNode
    if(!g_terrain.IsWall(nextX, nextY))
    {       
        Map[nextX][nextY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
        Map[nextX][nextY].direction.x = Map[nextX][nextY].parent->direction.x;
        Map[nextX][nextY].direction.y = Map[nextX][nextY].parent->direction.y;
        Map[nextX][nextY].heurCost = GetHeuristicCost(nextX, nextY);
        // Movement cost for diagonals is square root of 2
        Map[nextX][nextY].movCost = currentNode.movCost + 1.0f;
        Map[nextX][nextY].pos.x = nextX;
        Map[nextX][nextY].pos.y = nextY;
        if ((Map[nextX][nextY].movCost + Map[nextX][nextY].heurCost) < (currentNode.movCost + currentNode.heurCost))
        {
            output.push_back(Map[nextX][nextY]);
        }
    }

    int dirX = currentNode.direction.x;
    int dirY = currentNode.direction.y;

    // If we are moving diagonally, add the adjacent nodes in the same direction
    if(dirX!= 0 && dirY!=0)
    {
        if((dirY == +1) &&(!g_terrain.IsWall(currentNode.pos.x+ neighborX[TOP], currentNode.pos.y+ neighborY[TOP])))
        {
            int naturalNeighborX = currentNode.pos.x+ neighborX[TOP];
            int naturalNeighborY = currentNode.pos.y+ neighborY[TOP];
            Map[naturalNeighborX][naturalNeighborY].direction.x = neighborX[TOP];
            Map[naturalNeighborX][naturalNeighborY].direction.y = neighborY[TOP];
            Map[naturalNeighborX][naturalNeighborY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
            Map[naturalNeighborX][naturalNeighborY].heurCost = GetHeuristicCost(nextX, nextY);
            // Movement cost for diagonals is square root of 2
            Map[naturalNeighborX][naturalNeighborY].movCost = currentNode.movCost + 1.41421f;
            Map[naturalNeighborX][naturalNeighborY].pos.x = naturalNeighborX;
            Map[naturalNeighborX][naturalNeighborY].pos.y = naturalNeighborY;
            if ((Map[naturalNeighborX][naturalNeighborY].movCost + Map[naturalNeighborX][naturalNeighborY].heurCost) < (currentNode.movCost + currentNode.heurCost))
            {
                output.push_back(Map[naturalNeighborX][naturalNeighborY]);
            }
        }
        else if(!g_terrain.IsWall(currentNode.pos.x+ neighborX[BOTTOM], currentNode.pos.y+ neighborY[BOTTOM]))
        {
            int naturalNeighborX = currentNode.pos.x+ neighborX[BOTTOM];
            int naturalNeighborY = currentNode.pos.y+ neighborY[BOTTOM];
            Map[naturalNeighborX][naturalNeighborY].direction.x = neighborX[BOTTOM];
            Map[naturalNeighborX][naturalNeighborY].direction.y = neighborY[BOTTOM];
            Map[naturalNeighborX][naturalNeighborY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
            Map[naturalNeighborX][naturalNeighborY].heurCost = GetHeuristicCost(nextX, nextY);
            // Movement cost for diagonals is square root of 2m
            Map[naturalNeighborX][naturalNeighborY].movCost = currentNode.movCost + 1.41421f;
            Map[naturalNeighborX][naturalNeighborY].pos.x = nextX;
            Map[naturalNeighborX][naturalNeighborY].pos.y = nextY;
            if ((Map[naturalNeighborX][naturalNeighborY].movCost + Map[naturalNeighborX][naturalNeighborY].heurCost) < (currentNode.movCost + currentNode.heurCost))
            {
                output.push_back(Map[naturalNeighborX][naturalNeighborY]);
            }
        }

        if((dirX == +1) && (!g_terrain.IsWall(currentNode.pos.x+ neighborX[RIGHT], currentNode.pos.y+ neighborY[RIGHT])))
        {
            int naturalNeighborX = currentNode.pos.x+ neighborX[RIGHT];
            int naturalNeighborY = currentNode.pos.y+ neighborY[RIGHT];
            Map[naturalNeighborX][naturalNeighborY].direction.x = neighborX[RIGHT];
            Map[naturalNeighborX][naturalNeighborY].direction.y = neighborY[RIGHT];
            Map[naturalNeighborX][naturalNeighborY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
            Map[naturalNeighborX][naturalNeighborY].heurCost = GetHeuristicCost(nextX, nextY);
            // Movement cost for diagonals is square root of 2
            Map[naturalNeighborX][naturalNeighborY].movCost = currentNode.movCost + 1.41421f;
            Map[naturalNeighborX][naturalNeighborY].pos.x = naturalNeighborX;
            Map[naturalNeighborX][naturalNeighborY].pos.y = naturalNeighborY;
            if ((Map[naturalNeighborX][naturalNeighborY].movCost + Map[naturalNeighborX][naturalNeighborY].heurCost) < (currentNode.movCost + currentNode.heurCost))
            {
                output.push_back(Map[naturalNeighborX][naturalNeighborY]);  
            }
        }
        else if(!g_terrain.IsWall(currentNode.pos.x+ neighborX[LEFT], currentNode.pos.y+ neighborY[LEFT]))
        {
            int naturalNeighborX = currentNode.pos.x+ neighborX[LEFT];
            int naturalNeighborY = currentNode.pos.y+ neighborY[LEFT];
            Map[naturalNeighborX][naturalNeighborX].direction.x = neighborX[LEFT];
            Map[naturalNeighborX][naturalNeighborX].direction.y = neighborX[LEFT];
            Map[naturalNeighborX][naturalNeighborX].parent = &Map[currentNode.pos.x][currentNode.pos.y];
            Map[naturalNeighborX][naturalNeighborX].heurCost = GetHeuristicCost(nextX, nextY);
            // Movement cost for diagonals is square root of 2
            Map[naturalNeighborX][naturalNeighborY].movCost = currentNode.movCost + 1.41421f;
            Map[naturalNeighborX][naturalNeighborY].pos.x = nextX;
            Map[naturalNeighborX][naturalNeighborY].pos.y = nextY;
            if ((Map[naturalNeighborX][naturalNeighborY].movCost + Map[naturalNeighborX][naturalNeighborY].heurCost) < (currentNode.movCost + currentNode.heurCost))
            {
                output.push_back(Map[naturalNeighborX][naturalNeighborY]);
            }
        }
    }

}


void Movement::GetForcedNeighbors(std::vector<Node>& output, Node& currentNode)
{
    int dir_x = currentNode.direction.x;
    int dir_y = currentNode.direction.y;

    int nextNodeX = currentNode.pos.x + dir_x;
    int nextNodeY = currentNode.pos.y + dir_y;


    // Forced neighbors only exist for diagonal moves
    if((dir_x != 0) && (dir_y != 0))
    {
        // Check for diagonal forced neighbors

        if(dir_x == +1)
        {
            // Does the top right diagonal have any forced neighbors ?
            if(g_terrain.IsWall(nextNodeX + neighborX[TOP_RIGHT], nextNodeY + neighborY[TOP_RIGHT]) && !g_terrain.IsWall(nextNodeX + neighborX[TOP], nextNodeY + neighborY[TOP]))
            {   
                Map[nextNodeX][nextNodeY].direction.x = neighborX[TOP];
                Map[nextNodeX][nextNodeY].direction.y = neighborY[TOP];
                Map[nextNodeX][nextNodeY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
                Map[nextNodeX][nextNodeY].heurCost = GetHeuristicCost(nextNodeX, nextNodeY);
                // Movement cost for diagonals is square root of 2
                Map[nextNodeX][nextNodeY].movCost = currentNode.movCost + 1.41421f;
                Map[nextNodeX][nextNodeY].pos.x = nextNodeX;
                Map[nextNodeX][nextNodeY].pos.y = nextNodeY;

                if ((Map[nextNodeX][nextNodeY].movCost + Map[nextNodeX][nextNodeY].heurCost) < (currentNode.movCost + currentNode.heurCost))
                {
                    output.push_back(Map[nextNodeX][nextNodeY]);
                }
            }

            // Does the bottom right diagonal have any forced neighbors ?
            if(g_terrain.IsWall(nextNodeX + neighborX[BOTTOM_RIGHT], nextNodeY + neighborY[BOTTOM_RIGHT]) && !g_terrain.IsWall(nextNodeX + neighborX[BOTTOM], nextNodeY + neighborY[BOTTOM]))
            {   
                Map[nextNodeX][nextNodeY].direction.x = neighborX[BOTTOM];
                Map[nextNodeX][nextNodeY].direction.y = neighborY[BOTTOM];
                Map[nextNodeX][nextNodeY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
                Map[nextNodeX][nextNodeY].heurCost = GetHeuristicCost(nextNodeX, nextNodeY);
                // Movement cost for diagonals is square root of 2
                Map[nextNodeX][nextNodeY].movCost =  currentNode.movCost + 1.41421f;
                Map[nextNodeX][nextNodeY].pos.x = nextNodeX;
                Map[nextNodeX][nextNodeY].pos.y = nextNodeY;

                output.push_back(Map[nextNodeX][nextNodeY]);
            }

        }

        if(dir_x == -1)
        {
            // Does the top left diagonal have any forced neighbors ?
            if(g_terrain.IsWall(nextNodeX + neighborX[TOP_LEFT], nextNodeY + neighborY[TOP_LEFT]) && !g_terrain.IsWall(nextNodeX + neighborX[TOP], nextNodeY + neighborY[TOP]))
            {
                Map[nextNodeX][nextNodeY].direction.x = neighborX[TOP];
                Map[nextNodeX][nextNodeY].direction.y = neighborY[TOP];
                Map[nextNodeX][nextNodeY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
                Map[nextNodeX][nextNodeY].heurCost = GetHeuristicCost(nextNodeX, nextNodeY);
                // Movement cost for diagonals is square root of 2
                Map[nextNodeX][nextNodeY].movCost = ((dir_x == 0) || (dir_y == 0))? currentNode.movCost + 1.0f: currentNode.movCost + 1.41421f;
                Map[nextNodeX][nextNodeY].pos.x = nextNodeX;
                Map[nextNodeX][nextNodeY].pos.y = nextNodeY;

                output.push_back(Map[nextNodeX][nextNodeY]);
            }

            // Does the bottom left diagonal have any forced neighbors ?
            if(g_terrain.IsWall(nextNodeX + neighborX[BOTTOM_LEFT], nextNodeY + neighborY[BOTTOM_LEFT]) && !g_terrain.IsWall(nextNodeX + neighborX[BOTTOM], nextNodeY + neighborY[BOTTOM]))
            {
                Map[nextNodeX][nextNodeY].direction.x = neighborX[BOTTOM];
                Map[nextNodeX][nextNodeY].direction.y = neighborY[BOTTOM];
                Map[nextNodeX][nextNodeY].parent = &Map[currentNode.pos.x][currentNode.pos.y];
                Map[nextNodeX][nextNodeY].heurCost = GetHeuristicCost(nextNodeX, nextNodeY);
                // Movement cost for diagonals is square root of 2
                Map[nextNodeX][nextNodeY].movCost = ((dir_x == 0) || (dir_y == 0))? currentNode.movCost + 1.0f: currentNode.movCost + 1.41421f;
                Map[nextNodeX][nextNodeY].pos.x = nextNodeX;
                Map[nextNodeX][nextNodeY].pos.y = nextNodeY;

                output.push_back(Map[nextNodeX][nextNodeY]);
            }
        }
    }
}

其他一切都遵循原始文档的逻辑,但这两个功能是我不确定的唯一两个。创建者和许多教程都没有深入解释如何识别它们。

我的 A* 实现将世界信息存储在称为 Map 的 2D 矩阵中,而不是使用封闭列表。打开列表只是要打开哪些节点的向量。节点包含它们的 x 和 y 位置、启发式成本和移动成本(通常称为给定成本)以及玩家踩到瓷砖时所面对的方向。玩家只在 x 和 y 上移动。

我为文字墙道歉,但这是我的第一篇文章,我想发布尽可能多的相关信息。

4

0 回答 0