-4

我正在尝试在 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 的代码。

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

4

1 回答 1

1

否决票可能是由于不完整的代码示例的组合,以及自己调试问题的最小努力。但是,到底是什么......我将提供尽可能多的信息作为基于发布的问题的答案......

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

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

此外,虽然无法验证您的声明(由于代码示例不完整),但我们也可以假设oCount发生此异常时不具有值 0 是真的(如果您更具体;描述什么真实的比描述一些不真实的随机事实有用得多

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

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

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

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

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

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

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

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

如果以上内容没有让您指出正确的方向并且您确实需要帮助,那么您需要发布一个好的代码示例。请参阅如何创建最小、完整和可验证的示例

于 2015-01-03T01:13:48.233 回答