检查矩形列表和单个Rectangle之间交集的最快方法

时间:2015-11-20 15:36:25

标签: c# iteration game-engine

我想检查矩形(A Player)是否与列表中的一个矩形(List)相交。

我目前正在使用for循环,这使得它变慢,性能不佳。

for (int i = 0; i < gameObjects.objectList.Count; i++)
{
   if (gameObjects.objectList[i].IntersectsWith(gameObjects.player))
   {
        gameObjects.objectList.RemoveAt(i); // if the player collided with an object, remove that object
    }
}

如何让它更有效率/还有另一种方法可以更快地完成它吗?

3 个答案:

答案 0 :(得分:0)

您可以尝试在名为 k-d-tree 的结构中组织矩形。

它可以在大型矩形数组(> 100)中提供高达O(log N)的复杂度。

F.e。制作具有固定长度的二叉树,比方说,2。将你的空间分成左右两半,然后将每一半划分为顶部和底部四分之一(依此类推)。

Inside leaf node在矩形上创建一个列表。如果矩形落入左半部分和顶部四分之一,请在本季度列表中找到它。

矩形可能同时位于几个列表中(例如,如果它位于左右两半)。

要检查交叉点,您应该在响应的半部和四分之一处测试矩形。

或者,您删除了太多的矩形,将剩余的矩形复制到您自己的代码中的新列表中会更快。

小例子。

public enum CheckBy
{
    Horizontal,

    Vertical
}

public class Node
{
    public Node First { get; set; }

    public Node Second { get; set; }

    public int Coordinate { get; set; }

    public CheckBy CheckBy { get; set; }

    public List<Rectangle> Rectangles { get; set; }
}

public bool IsRectangleInFist(Node node, Rectangle rectangle)
{
    if (node.CheckBy == CheckBy.Horizontal)
        return rectangle.Left <= node.Coordinate;

    return rectangle.Top <= node.Coordinate;
}

public bool IsRectangelInSecond(Node node, Rectangle rectangle)
{
    if (node.CheckBy == CheckBy.Horizontal)
        return rectangle.Right >= node.Coordinate;

    return rectangle.Bottom >= node.Coordinate;
}

public void AddRectangleInSuitableNode(Node node, Rectangle rectangle)
{
    if (InRectangleInFirst(node, rectangle))
        AddRectangleInSuitableNode(node.First, rectangle);

    if (InRectangleInSecond(node, rectangle))
        AddRectangleInSuitableNode(node.Second, rectangle);
}

public void SearchIntersectedRectangles(Node node, Rectangle rectangle, List<Rectangles> result)
{
    // If not-leaf node
    if (node.Rectangles == null && node.First != null && node.Second != null)
    {
        if (IsRectangleInFirst(node, rectangle))
            SearchIntersecatedRectangles(node.First, rectangle, result);

        if (IsRectangleInSecond(node, rectangle))
            SearchIntersecatedRectangles(node.Second, rectangle, result);

        return;
    }

    result.AddRangle(Rectangles.Where(r => r.IsIntersect(rectangle)));
}

这些所有行都是简单的2D树。首先,制作树:

// Say, all rectangles would be inside this "space"
const int leftest = -1000;
const int rightest = 1000;
const int bottomest = -1000;
const int toppest = 1000;

// Tree with depth == 2
var tree = new Node
{
    CheckBy = CheckBy.Hozirontal,
    Coordinate = (leftest + rightest)/2,
    First = new Node
    {
        CheckBy = CheckBy.Vertical,
        Coordintate = (toppest + bottomest)/2,
        Rectangles = new List<Rectangle>(),
    },
    Second = new Node
    {
        CheckBy = CheckBy.Vertical,
        Coordintate = (toppest + bottomest)/2,
        Rectangles = new List<Rectangle>(),
    },
}

然后,对此树中的所有矩形进行排序:

foreach (var rectangle in rectangles)
    AddRectangleInSuitableNode(tree, rectangle);

现在你可以快速获得相交的矩形:

var intersecting = new List<Rectangles>();
SearchIntersecatedRectangles(tree, targetRectangle, intersecting);
// Here you can remove intersecting rectangles...

答案 1 :(得分:0)

基本上,您需要每次都停止检查所有矩形。您需要以某种方式确定哪些矩形位于播放器附近。

您可以使用某种空间网格来存储矩形,以便快速找到要检查碰撞的相邻矩形。请参阅本教程,例如:N Tutorial B - Broad-Phase Collision

答案 2 :(得分:0)

我怀疑它会更快但你可以随时用一个班轮做到这一点:

gameObjects.objectList = gameObjects.objectList
                         .Select(go => go)
                         .Where(go => !go.IntersectsWith(gameObjects.player))
                         .ToList();

这实质上将列表设置为与gameObject发生碰撞的player被移除的列表。

另请注意,首先处理排序列表通常会更快,因此请执行以下操作:

gameObjects.objectList = gameObjects.objectList
                         .OrderBy(go => go.X)
                         .ThenBy(go => go.Y)
                         .ToList();

可能有助于加快速度。每个帧执行此排序的速度会很慢,因此在将对象添加到列表中时对它们进行排序是值得的。