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

Unity: How to find all neighbors recursively (neighbors of neighbors)?

我正在用 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;
}

我预计一行中的所有气泡都会被破坏。我收到堆栈溢出错误。如果我删除它的递归部分,但只适用于直接邻居。

错误是这样的:WhosebugException:请求的操作导致堆栈溢出,它在我再次调用 FindAllRecursiveNeighbors 的行中。

你的模式是正确的。我相信堆栈溢出异常是由 returning 到一个已经访问过的节点引起的。

您可能想要保留一个已经访问过的节点的列表,然后不要将它们 return 作为邻居。您已经拥有所需的东西,只需要进行装配即可。下面的代码可能对你有用

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 函数不是 return 当前节点,您应该将当前节点添加到列表中。

作为最终解释,这里发生的是: 假设您有 3 个这样的节点 1 <-> 2 <-> 3 现在你从 1 开始,去检查 2 的邻居,你找到 3,然后又是 1,你再次进入循环检查 1 的邻居,这个循环继续。

解决此类任务的最佳方法是拥有一个未搜索节点的列表,并从中选择很少的递归。但是,在这里我保留了一个已经访问过的节点的列表,这可能会变得很重,具体取决于您要在单个操作中分解的节点数量。但这对您的原始代码更改较少。