Unity:如何递归查找所有邻居(邻居的邻居)?

时间:2019-11-09 10:00:47

标签: c# unity3d recursion stack-overflow nearest-neighbor

我正在Unity中制作一个泡泡射击游戏。我到了可以再碰到一个泡泡的地步,它摧毁了它的所有邻居。

现在,我正在尝试销毁其所有邻居的邻居,这会导致堆栈溢出。我正在使用递归。

我没有递归地做到了,发现它的第二层邻居只是为了看逻辑是否可行。是的问题出在我使用递归的方式上。

    private List<Bubble> FindAllRecursiveNeighbors(Vector2Int originPosition)
{
    List<Bubble> allNeighbors = FindNeighbors(originPosition);

    List<Bubble> result = new List<Bubble>();

    foreach (Bubble bubble in allNeighbors)
    {
        if (result.Contains(bubble)) { continue; }
        result.Add(bubble);
    }

    // Recursion starts here.
    foreach (Bubble bubble in result)
    {
        List<Bubble> neighbors = FindAllRecursiveNeighbors(FindPositionOfBubble(bubble));
        foreach (Bubble neighbor in neighbors)
        {
            if (result.Contains(neighbor)) { continue; }
            result.Add(neighbor);
        }
    }

    return result;
}

我希望生产线上的所有气泡都将被销毁。我收到堆栈溢出错误。如果我删除递归部分,那么它会起作用,但仅适用于直接邻居。

错误是这样的:StackOverflowException:请求的操作导致了堆栈溢出,并且在我再次调用FindAllRecursiveNeighbors的那一行中。

1 个答案:

答案 0 :(得分:0)

您的模式是正确的。我相信堆栈溢出异常是由返回到已经访问过的节点引起的。

您可能想保留一个已访问节点的列表,然后不要将其作为邻居返回。您已经有了所需的东西,只需要组装就可以了。下面的代码可能对您有用

private List<Bubble> FindAllRecursiveNeighbors(Vector2Int originPosition, List<Bubble> result = null)
{
    List<Bubble> allNeighbors = FindNeighbors(originPosition);

    if (result == null)
        // Mark (see bellow)
        result = new List<Bubble>();

    var newBubbles = new List<Bubble>();
    foreach (Bubble bubble in allNeighbors)
    {
        if (!result.Contains(bubble))
        {
            result.Add(bubble);
            newBubbles.Add(bubble);
        }
    }

    // Recursion starts here.
    foreach (Bubble bubble in newBubbles)
    {
        List<Bubble> neighbors = FindAllRecursiveNeighbors(FindPositionOfBubble(bubble), result);
    }

    return result;
}

虽然我不清楚一件事。查看我在代码中标记的位置。如果您的FindNeighbors函数未返回当前节点,则应该在此处添加当前节点。

最后的解释是: 假设您有3个这样的节点1 <-> 2 <-> 3 现在,您从1开始,去检查2的邻居,再找到3,再去1,则在循环中再次去检查1的邻居,这个循环继续。

解决此类任务的最佳方法是拥有未搜索节点的列表,并从中进行递归很少。但是,在这里,我保留了一个已访问节点的列表,该列表可能会很繁琐,具体取决于您希望在单个操作中爆炸的节点数量。但这对原始代码的更改较少。