【问题标题】:Making wider rectangles from continuous fixed size rectangles?从连续固定大小的矩形制作更宽的矩形?
【发布时间】:2014-01-07 04:45:07
【问题描述】:

我正在为我的游戏添加 Tile 碰撞。

我所做的是,遍历每个对象并获取它们周围的边缘图块。

我返回一个 Vec2 的向量,它对应于每个周围边缘瓦片的左上角位置。

那些被放入一组 Vec2 中,所以只有唯一的保留。

从那里,可以构建固定大小的矩形。

但是,我要求将连续的矩形合并为 1。

所以在这个例子中,我们会将这 8 个固定的矩形变成 3 个。

最右边的会保持原样。

x 轴上的 6 会变成 1,x 轴上最左边和最下面的会变成 1。

鉴于我有一个集合 Vec2 表示每个平铺正方形的左上角位置并且我知道正方形的宽度,我如何计算一个新的 Rectangle(x,y,w,h) 向量与合并的固定正方形?

    void TilePhysicsManager::update()
        {
            m_locationSet.clear(); //clear the unique locations

            for (b2Body* b = m_b2world->GetBodyList(); b; b = b->GetNext())
            {
                if(b->GetType() == b2_dynamicBody)
                {
                    PhysicsObject* obj = (PhysicsObject*)b->GetUserData();
                    const std::vector<Vec2>& edgeTiles = m_tileWorld->getSideTiles(*obj,1.5f);

                    //add in locations (duplicates will be rejected)
                    for(int i = 0; i < edgeTiles.size(); ++i)
                    {
                        m_locationSet.insert(edgeTiles[i]);
                    }

                }
            }

            int objIndex = 0; //index of which dummy object we need

            for(std::set<Vec2>::iterator it = m_locationSet.begin(); it != m_locationSet.end(); ++it)
            {
                //if our memory pool is not big enough, grow it
                if(objIndex >= count())
                {
                    allocNewObject();
                }

                m_objects[objIndex]->setLocation(*it);
                objIndex++;
            }
}

谢谢

【问题讨论】:

  • 那些连续的固定矩形几乎总是几乎相连吗?您是否需要从那些较小的矩形中生成最小的较宽矩形,或者有时接近最小数量的较宽矩形也是可以接受的?
  • @invisal 它必须是最长的连续带,否则玩家会卡在边缘。
  • 最长的垂直连续带还是最长的水平连续带?哪一个?
  • @invisal 它需要同时生成它们,但它们是边缘图块,因此您最多只能拥有生成垂直和水平形状的 L 形状。

标签: c++ algorithm stl


【解决方案1】:

我相信最简单快捷的方法是在“垂直连续带”扩展之后进行“水平连续带”扩展,反之亦然。

例如:

  • 计算边界矩形。垂直条的数量是高度除以固定矩形的高度。
  • 创建一个包含多个条形元素的数组。存储每个条带的最小和最大 X 坐标。使用最小和最大坐标来构建更宽的矩形。

  • 然后,应用水平条扩展。 (在我们的示例中,垂直条展开后我们将有 4 个矩形)。

  • 创建一个包含多个条形元素的数组。存储每个条带的最小和最大 Y 坐标。使用最小和最大坐标来构建更宽的矩形。
  • 检查较宽的垂直条是否可以进一步扩大,如果可以,扩大。

结论:

  • 此算法假定每个条带中没有不连续性。

实施

  • 以下是C#中的优化实现

    private List<Rectangle> Merge(Rectangle[] r) {
        // Computing the bound
        Rectangle bound = new Rectangle(r[0].X, r[0].Y, r[0].Width, r[0].Height);
        for (int i = 1; i < r.Length; ++i) {
            bound.X = Math.Min(bound.X, r[i].X);
            bound.Y = Math.Min(bound.Y, r[i].Y);
            bound.Width = Math.Max(bound.Right, r[i].Right) - bound.X;
            bound.Height = Math.Max(bound.Bottom, r[i].Bottom) - bound.Y;
        }
    
        // Create number of rectangle will be created by vertical strip expansion
        Rectangle[] m = new Rectangle[bound.Height / RECT_HEIGHT];
        for (int i = 0; i < m.Length; ++i) {
            m[i] = new Rectangle(Int32.MaxValue, bound.Y +  i * RECT_HEIGHT, 0, RECT_HEIGHT);
        }
    
        for (int i = 0; i < r.Length; ++i) {
            int index = (r[i].Y - bound.Y) / RECT_HEIGHT;
    
            if (m[index].Width <= 0) {
                m[index].Width = r[i].Width;
                m[index].X = r[i].X;
            } else {
                int right = m[index].Right;
                m[index].X = Math.Min(m[index].X, r[i].X);
                m[index].Width = Math.Max(right, r[i].Right) - m[index].X;
            }
        }
    
        // Merge horozontally
        for (int i = m.Length - 1; i > 0; --i) {
            // can only merge when two rect has the same X and Width
            if ((m[i].X == m[i - 1].X) && (m[i].Width == m[i - 1].Width)) {
                m[i - 1].Height += m[i].Height; // expanse the rectangle
                m[i].Width = 0;                // remove one rectangle
            }
        }
    
        // Remove all the empty rectangle
        List<Rectangle> result = new List<Rectangle>();
        for (int i = m.Length - 1; i >= 0; --i) {
            if (m[i].Width > 0)
                result.Add(m[i]);
        }
    
        return result;
    }
    

以下是通过以下实现生成的结果。左边是初始矩形,右边是结果。

  • 如果可以合并两个垂直框,请创建一个更大的矩形。

  • 垂直框优先于水平框

  • 在一个垂直条中的两个框之间存在中断空间。结果并非我们预期的那样。

【讨论】:

  • 在您展示图片的情况下,有什么方法可以使垂直的延伸并与水平的相交(其顶部坐标将延伸 1 个正方形),否则我会遇到同样的问题,只是不那么频繁。谢谢
  • 我不明白你的问题。算法保证的是它将返回更宽的矩形,它们之间没有任何交集。因为,我们首先先垂直扩展,如果它是可扩展的,然后再水平扩展。我们如何知道两个矩形是否可以水平合并在一起?我们可以检查这两个是否具有相等的宽度和左侧位置。如果你看最后一张图片,下面的矩形不能和中间的矩形合并,因为它们有不同的宽度。
  • @Milo,就像在我的帖子中一样,该算法假设每个垂直条中没有不连续性。如果你仔细检查实现,你就会明白为什么。
  • 好吧,一旦矩形都制作好了,你可以这样做:对于每个水平框的每个垂直框,如果 vertical.topleft == Horizo​​ntal.bottomleft 垂直向上延伸水平高度
  • @Milo,我已经更新了实现并添加了更多演示图像。
【解决方案2】:

我认为问题陈述有一点模糊性需要解决。在给出的示例中,有多种可能的方法可以将给定的图块分组为最大的、不重叠的较大矩形。具体来说,左上角的图块可以按照您的建议与该行的其余部分合并,也可以与其下方的图块合并。

就算法而言,我首先想到的是从集合中一个一个地取出图块并通过它们的邻居扩展它们。当扩展多于一个图块的矩形时,只需检查该边缘上的所有图块在单个集合中(剩余部分)中是否有邻居。一旦你发现矩形无法向任何方向扩展,你就完成了,可以移动到下一个图块。

这种方法对我来说似乎大约为 O(n^2),这可能会有所改进,但在实践中很容易成为非问题,具体取决于规模和您需要多久评估一次(使用静态级别几何,我不希望经常这样做)。

使用这种方法需要注意的一点是,不要在迭代容器时犯错误从容器中删除元素。错误的迭代器 juju。只需复制一份即可。

【讨论】:

  • 不幸的是,我需要每帧计算一次。
  • 还是可以的。真的取决于 n 有多大。好的,不过,假设我们想让它更快。嗯,我想到的第一件事实际上是通过它们的 y 坐标来遍历并分桶瓷砖。然后,在每个桶内(按 x 排序),进行线性扫描以找到连续的条带。也许这些条带,即使不是垂直最大的,也足以满足您的目的?如果没有,识别可以合并的相邻条带应该不会有太多额外的工作。只需按最左边的 x 坐标对条带进行排序,然后向右扫描,在找到它们时将它们组合起来。
猜你喜欢
  • 2019-04-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-16
  • 2011-10-18
  • 2021-10-25
  • 2021-01-10
相关资源
最近更新 更多