【问题标题】:Index out of range error when implementing A-star path-finding algorithm [closed]实现 A-star 寻路算法时出现索引超出范围错误 [关闭]
【发布时间】:2015-01-02 22:57:11
【问题描述】:

我正在尝试在 2D RPG 游戏(基于网格的环境)中创建角色 AI 运动。使用 A* 寻路算法,我已经成功地为角色创建了不同的路径(代表他的运动)但是当我运行/调试我的游戏时我遇到了一个反复出现的问题,我似乎无法解决:

由于我的“openList”中的元素数量随机变为 0(变量:oCount),因此出现索引超出范围错误。我已经调试了很多代码,试图找到 openList 丢失其元素的确切位置,但没有任何运气。我已将其范围缩小到 while 循环的末尾。为了测试这一点,我使用了以下 if 语句并在其中放置了一个断点:

if (oCount == 0 || openList.Count == 0)
    oCount = 0; //breakpoint added here 

如果您在我的代码中看到“//**”,这就是我放置上述“if 语句”进行测试的地方。

我对编程很陌生,所以请多多包涵。

这是我制作的 AI 类的代码:

class AI
{
    List<Tile> openList;
    List<Tile> closedList;
    List<int> path; //represents the shortest path to travel in

    int cCount = 0;         // no. of elements in closedList
    int oCount = 0;         // no. of elements in openList

    public AI()
    {
        openList = new List<Tile>();
        closedList = new List<Tile>();
        path = new List<int>();
    }

    public List<int> FindPath(Tile[,] tile, Vector2 xBoundary, Vector2 yBoundary, Vector2 startTile, Vector2 endTile)
    {
        // [tile = elements of this array tell me the grid-position of the tile] 
        // [xBoundary + yBoundary = the rectangular boundary for where the character can move]
        // [startTile + endTile = the grid-location of the target tile and the tile the player is currently on]

        //Reset variables
        openList.Clear();
        closedList.Clear();
        path.Clear();
        tile[(int)startTile.X, (int)startTile.Y].PDir = 0; //[PDir = direction to parent tile]

        //Add starting tile to openList
        openList.Add(tile[(int)startTile.X, (int)startTile.Y]);

        //Set scores for starting tile
        openList[0].G = 0;
        openList[0].H = (int)Math.Abs(endTile.X - startTile.X) + (int)Math.Abs(endTile.Y - startTile.Y);
        openList[0].F = openList[0].G + openList[0].H;

        //** Error occurred without debugger entering the 'if statement' here


        while (path.Count == 0)
        {
            oCount = openList.Count;

            //**

            //Sort openList by F score (i.e last element will have the lowest F score)
            openList = Sort(openList);

            //**

            //Move tile with lowest F score from openList to closedList
            closedList.Add(openList[oCount - 1]); //ERROR OCCURS ON THIS LINE!!
            openList.RemoveAt(oCount - 1);
            cCount = closedList.Count;


            //Add valid surrounding tiles to openList AND Alter valid existing tiles (I will condense this into a method later, when everything is working)
            //----------------------------------------------------------------------------------------------
            // Each following for-loop checks the two nearest x- or y- grid positions

            // a = difference in grid x-position
            for (int a = -1; a < 2; a += 2)
            {
                //If tile is walkable[1], not in closed list[2], and within set boundaries[3-4]...
                if (tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].Collision == false
                    && !closedList.Contains(tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y])
                    && closedList[cCount - 1].X + a >= xBoundary.X && closedList[cCount - 1].X + a <= xBoundary.Y      
                    && closedList[cCount - 1].Y >= yBoundary.X && closedList[cCount - 1].Y <= yBoundary.Y)
                {
                    //If tile not in open list...
                    if (!openList.Contains(tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y]))
                    {
                        //Add tile to openList
                        openList.Add(tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y]);
                        oCount = openList.Count;

                        //Set direction to parent tile
                        if (a == -1)
                            tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].PDir = 1;
                        else if (a == 1)
                            tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].PDir = 3;

                        //Calculate F, G and H
                        openList[oCount - 1].G = closedList[cCount - 1].G + 1;
                        openList[oCount - 1].H = (int)Math.Abs(endTile.X - openList[oCount - 1].X) + (int)Math.Abs(endTile.Y - openList[oCount - 1].Y);
                        openList[oCount - 1].F = openList[oCount - 1].G + openList[oCount - 1].H;

                    }
                    //otherwise, check if current path is better than the previous path that tile had...
                    else if (closedList[cCount - 1].G + 1 < tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].G)
                    {
                        //Set new direction to parent tile
                        if (a == -1)
                            tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].PDir = 1;
                        else if (a == 1)
                            tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].PDir = 3;

                        //Re-calculate G and H values
                        int g = closedList[cCount - 1].G + 1;
                        int h = (int)Math.Abs(endTile.X - tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].X) + (int)Math.Abs(endTile.Y - tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].Y);

                        //Set values to tile
                        tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].G = g;
                        tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].H = h;
                        tile[closedList[cCount - 1].X + a, closedList[cCount - 1].Y].F = g + h;     //including f-value...

                        //**
                    }
                }
            }

            // b = difference in grid y-position
            for (int b = -1; b < 2; b += 2)
            {
                //If tile is walkable, not in closed list, and within set boundaries...
                if (tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].Collision == false
                    && !closedList.Contains(tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b])
                    && closedList[cCount - 1].X >= xBoundary.X && closedList[cCount - 1].X <= xBoundary.Y
                    && closedList[cCount - 1].Y + b >= yBoundary.X && closedList[cCount - 1].Y + b <= yBoundary.Y)
                {
                    //If tile not in open list...
                    if (!openList.Contains(tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b]))
                    {
                        //Add tile to openList
                        openList.Add(tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b]);
                        oCount = openList.Count;

                        //Set new parent tile direction
                        if (b == -1)
                            tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].PDir = 2;
                        else if (b == 1)
                            tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].PDir = 4;

                        //Calculate F, G and H
                        openList[oCount - 1].G = closedList[cCount - 1].G + 1;
                        openList[oCount - 1].H = (int)Math.Abs(endTile.X - openList[oCount - 1].X) + (int)Math.Abs(endTile.Y - openList[oCount - 1].Y);
                        openList[oCount - 1].F = openList[oCount - 1].G + openList[oCount - 1].H;

                    }
                    //otherwise, check if current path is better than the previous path that tile had...
                    else if (closedList[cCount - 1].G + 1 < tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].G)
                    {
                        //Set new parent tile direction
                        if (b == -1)
                            tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].PDir = 2;
                        else if (b == 1)
                            tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].PDir = 4;

                        //Re-calculate G and H values
                        int g = closedList[cCount - 1].G + 1;
                        int h = (int)Math.Abs(endTile.X - tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].X) + (int)Math.Abs(endTile.Y - tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].Y);

                        //Set values to tile
                        tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].G = g;
                        tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].H = h;
                        tile[closedList[cCount - 1].X, closedList[cCount - 1].Y + b].F = g + h;     //including f-value...

                        //**
                    }
                }
            }

            //----------------------------------------------------------------------------------------------


            //If end tile is reached (i.e at end of path)...
            if (closedList[cCount - 1].Y == endTile.Y && closedList[cCount - 1].X == endTile.X)
            {
                //tile[(int)endTile.X, (int)endTile.Y].PDir = 0;
                path = PlotPath(tile, tile[(int)startTile.X, (int)startTile.Y], tile[(int)endTile.X, (int)endTile.Y]); //plot the shortest route/path
            }
        }

        //** Breakpoint here tells me oCount reaches 0

        return path;
    }

    private List<Tile> Sort(List<Tile> list)
    {
        Tile temp;
        bool swapMade = true;           //tells whether a swap was made during an iteration
        short a = 0;

        while (swapMade)
        {
            //reset
            swapMade = false;
            a++;

            for (int i = list.Count - 1; i >= 0 + a; i--)
            {
                if (list[i].F > list[i - 1].F)
                {
                    //swap
                    temp = list[i];
                    list[i] = list[i - 1];
                    list[i - 1] = temp;

                    swapMade = true;
                }
            }
        }

        return list;
    }

    private List<int> PlotPath(Tile[,] tileArray, Tile startingTile, Tile endingTile)
    {
        //**

        Tile currentTile = endingTile;
        List<int> pathList = new List<int>();

        //**

        while (currentTile.PDir != 0) // PDir of 0 indicates that we've reached the starting Tile (i.e the path has been fully plotted). When the character moves, it follows this path in reverse order
        {
            if (currentTile.PDir == 1)
            {
                pathList.Add(3);
                currentTile = tileArray[currentTile.X + 1, currentTile.Y];
            }
            else if (currentTile.PDir == 2)
            {
                pathList.Add(4);
                currentTile = tileArray[currentTile.X, currentTile.Y + 1];
            }
            else if (currentTile.PDir == 3)
            {
                pathList.Add(1);
                currentTile = tileArray[currentTile.X - 1, currentTile.Y];
            }
            else if (currentTile.PDir == 4)
            {
                pathList.Add(2);
                currentTile = tileArray[currentTile.X, currentTile.Y - 1];
            }
        }

        //**

        return pathList;
    }
}

编辑:感谢 Peter Duniho 向我解释我的问题中缺少的地方。这样,我添加了所有使用 oCount/openList 的代码。

希望我的代码清晰易读。我相信它有很多可以改进的地方。

【问题讨论】:

  • 我想知道我收到的反对票的原因是什么

标签: c# algorithm a-star indexoutofrangeexception


【解决方案1】:

否决票可能是因为代码示例不完整,而且您自己调试问题的工作量似乎很小。但是,到底是什么……我将提供尽可能多的信息,作为基于发布的问题的答案……

什么是已知的,什么可以从已知的东西中推断出来:

  • 失败发生在表达式openList[oCount - 1]
  • IndexOutOfRangeException 被抛出

此外,虽然无法验证您的声明(由于代码示例不完整),但我们不妨假设oCount 在发生此异常时确实不具有值 0(本来应该是对你来说更具体更好;描述什么是真的比描述一些随机的事实不是是真的有用得多)。

这个假设连同两个事实告诉我们oCount 的值非零并且大于列表的长度(根据您的陈述,此时列表的长度显然为零)。

  • 变量 oCount 仅在之前的两个语句中初始化为列表的长度
  • 唯一的干预语句是调用某个Sort() 方法

这两个事实,连同前面的推论,告诉我们,列表的长度在 oCount 的初始化和使用 oCount 的表达式之间发生了某种变化。

不幸的是,变量openList 显然不是局部变量(为什么?!这似乎是一个非常糟糕的主意),甚至没有在这个方法中初始化。因此无法从代码示例中看出该对象还有哪些地方可能被修改。

这给了我们至少两个可能的解释:

  1. Sort() 方法正在减少列表的长度
  2. 还有其他一些线程由于某种原因正在修改列表

如果我必须打赌,我猜#1 是问题所在。主要是因为这个问题甚至没有提到第二个线程。

最后,请注意:即使在初始化 oCount 时列表的长度为零,您仍然会遇到相同的异常,因为 oCount - 1 将是 -1 并且仍然超出有效索引值的范围对于列表(就此而言,0 将超出范围)。因此,上述内容很大程度上依赖于您声称oCount 为零,而列表长度实际上为零。

如果以上内容没有让您找到正确的方向并且您确实需要帮助,您需要发布一个好的代码示例。见How to create a Minimal, Complete, and Verifiable example

【讨论】:

  • 感谢您的信息。非常感激。我已经更新了这个问题,希望它现在符合标准。我还进一步缩小了问题的范围。希望这可以帮助。我也会考虑整理我的代码。仍然掌握它的窍门
  • 我也认为 Sort() 方法有问题,但是如您所见,事实并非如此。我在调用它之前和之后进行了调试,并且 oCount 保持不变。我也没有为 oCount 提供属性方法,因此在代码中的其他任何地方都没有更改它。这些都是与之相关的方法。如果我猜测,考虑到我的调试,问题应该出在两个 for 循环中的“else if”......但是这里没有改变 oCount。至于您的“最后说明”,openList 初始化为 0 个元素,但我们可以看到之后立即添加了新元素。
  • 我现在唯一能想到的是: 1. openList 被清除(0 个元素) 2. 元素添加到 openList (1) 3. 将元素从 openList 移动到 closedList (0) 4. '向 openList 添加有效的周围图块'的方法 -> 不添加任何内容(openList 保留 0 个元素) 5. 一旦我们到达它的末尾,就会再次启动 While 循环。尝试将 openList 中的元素移动到 closedList。错误..问题是,在这些错误期间有有效的瓷砖。并且错误可以立即发生,或者进入游戏 2 分钟。有了这个,似乎没有任何特定的瓷砖组导致问题
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-06
  • 1970-01-01
  • 2023-03-23
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多