【问题标题】:Fastest way to check intersection between a Rectangle List and a single Rectangle检查矩形列表和单个矩形之间交集的最快方法
【发布时间】:2015-11-21 00:34:08
【问题描述】:

我想检查一个 Rectangle(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
    }
}

我怎样才能提高效率/有没有其他方法可以更快地做到这一点?

【问题讨论】:

  • 一个for循环不慢,但是这段代码错了。
  • 列表的性质是什么?列表中的矩形是静态定义的吗?它们可能会相互重叠,如果是这样,应该会产生什么结果?
  • @Dennis_E ,你能解释一下有什么问题吗?我真的很想知道,所以我可以解决它
  • @Damien_The_Unbeliever,我每 X 次创建一个新矩形,将其添加到列表中,然后绘制它。 for 循环中缺少一些代码,实际上只是一行,表示将该对象绘制到位图。
  • 简单地从末尾开始而不是前面:for (int i = gameObjects.objectList.Count - 1; i &gt;= 0; i--)

标签: c# iteration game-engine


【解决方案1】:

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

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

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

在叶节点内部创建一个矩形列表。如果一个矩形属于左半边和上四分之一,请在本季度的列表中找到它。

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

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

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

小例子。

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)));
}

所有这些线构成了简单的二维树。首先,制作树:

// 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...

【讨论】:

  • @PazHaviv 我用一些示例代码更新了我的答案。
【解决方案2】:

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

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

【讨论】:

  • 我可以创建一个网格,然后检查该网格是否包含对象以及它们是否与播放器相交,但我怀疑它会产生什么不同,因为您需要每次检查网格是否播放器在 中,包含一个对象。所以基本上这一切都会以糟糕的表现告终
【解决方案3】:

我怀疑它会更快,但你总是可以在一个班轮中使用 Ling:

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

这实际上将列表设置为删除与player 冲突的任何gameObject 的列表。

还请注意,首先处理排序列表通常更快,所以这样做:

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

可能有助于加快速度。对每一帧进行这种排序会很慢,因此在将对象添加到列表时对其进行排序是值得的。

【讨论】:

  • LINQ 肯定会让事情变得更慢,尤其是当代码每帧都运行时
  • @EmpereurAiman 我知道它很可能会,但总是值得拥有一个替代品
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
  • 2013-03-09
  • 2011-10-31
相关资源
最近更新 更多