有没有一种好的方法可以在XNA中获得像素完美的碰撞检测?


12

XNA中是否有一种众所周知的方法(或者也许是可重用的代码)用于像素完美碰撞检测?

我假设这还将多边形(框/三角形/圆形)用于首过,碰撞的快速测试,如果该测试表明有碰撞,则它将搜索每个像素的碰撞。

这很复杂,因为我们必须考虑比例,旋转和透明度。

警告:如果您使用的是来自以下答案的链接中的示例代码,请注意,矩阵的缩放比例已被注释掉,这是有充分理由的。您无需取消注释即可开始扩展。


2
多边形还是精灵?
doppelgreener 2011年

我不确定。Xna同时支持?我的编辑提到了两者的结合,因为边界框测试是一种快速的首过方法。
ashes999 2011年

1
碰撞检测会因您使用3D / 2D模型还是精灵而有所不同。一个有像素。另一个具有顶点和边缘。
doppelgreener

好吧,我明白您现在得到的是什么。我正在使用精灵。尽管我相信XNA会将它们实现为带纹理的多边形。
ashes999 2011年

Answers:


22

我看到您将问题标记为2d,所以我将继续转储我的代码:

class Sprite {

    [...]

    public bool CollidesWith(Sprite other)
    {
        // Default behavior uses per-pixel collision detection
        return CollidesWith(other, true);
    }

    public bool CollidesWith(Sprite other, bool calcPerPixel)
    {
        // Get dimensions of texture
        int widthOther = other.Texture.Width;
        int heightOther = other.Texture.Height;
        int widthMe = Texture.Width;
        int heightMe = Texture.Height;

        if ( calcPerPixel &&                                // if we need per pixel
            (( Math.Min(widthOther, heightOther) > 100) ||  // at least avoid doing it
            ( Math.Min(widthMe, heightMe) > 100)))          // for small sizes (nobody will notice :P)
        {
            return Bounds.Intersects(other.Bounds) // If simple intersection fails, don't even bother with per-pixel
                && PerPixelCollision(this, other);
        }

        return Bounds.Intersects(other.Bounds);
    }

    static bool PerPixelCollision(Sprite a, Sprite b)
    {
        // Get Color data of each Texture
        Color[] bitsA = new Color[a.Texture.Width * a.Texture.Height];
        a.Texture.GetData(bitsA);
        Color[] bitsB = new Color[b.Texture.Width * b.Texture.Height];
        b.Texture.GetData(bitsB);

        // Calculate the intersecting rectangle
        int x1 = Math.Max(a.Bounds.X, b.Bounds.X);
        int x2 = Math.Min(a.Bounds.X + a.Bounds.Width, b.Bounds.X + b.Bounds.Width);

        int y1 = Math.Max(a.Bounds.Y, b.Bounds.Y);
        int y2 = Math.Min(a.Bounds.Y + a.Bounds.Height, b.Bounds.Y + b.Bounds.Height);

         // For each single pixel in the intersecting rectangle
         for (int y = y1; y < y2; ++y)
         {
             for (int x = x1; x < x2; ++x)
             {
                 // Get the color from each texture
                 Color a = bitsA[(x - a.Bounds.X) + (y - a.Bounds.Y)*a.Texture.Width];
                 Color b = bitsB[(x - b.Bounds.X) + (y - b.Bounds.Y)*b.Texture.Width];

                 if (a.A != 0 && b.A != 0) // If both colors are not transparent (the alpha channel is not 0), then there is a collision
                 {
                     return true;
                 }
             }
         }
        // If no collision occurred by now, we're clear.
        return false;
    }

    private Rectangle bounds = Rectangle.Empty;
    public virtual Rectangle Bounds
    {
        get
        {
            return new Rectangle(
                (int)Position.X - Texture.Width,
                (int)Position.Y - Texture.Height,
                Texture.Width,
                Texture.Height);
        }

    }

编辑:虽然这段代码几乎可以自我解释,但我确实很讨厌没有注释,所以我添加了一些;)我也摆脱了按位操作,因为它基本上是用Color.A属性以更复杂的方式进行操作;)


对没有评论或解释的代码转储投下反对票。
AttackingHobo

12
任何解释都将只是重新编写代码,并且代码相当简单。

2
我知道我在问题中提到了这一点,但我需要再次声明:这可以处理缩放和旋转吗?两个缩放旋转的精灵能否正确相交?(不过,我可以处理添加快速边界框的第一遍操作。)或者是否包含对的调用Bounds
ashes999 2011年

1
是的,我忘了!该代码未考虑转换。现在我没有时间来编辑,但你可以看看在碰撞转化样品,直到我做的:create.msdn.com/en-US/education/catalog/tutorial/...
PEK

1
只是学究式的,但您只需要:CollidesWith(Sprite other, bool calcPerPixel = true);:)
乔纳森·康奈尔

4

在App Hub上,有一个非常古老的示例,可带您完成2D碰撞检测的过程,从简单的边界框到对旋转和缩放的精灵进行像素测试。它已完全更新到4.0。如果您是该主题的新手,那么整个系列都值得一读。

http://xbox.create.msdn.com/zh-CN/education/catalog/tutorial/collision_2d_perpixel_transformed

我还发现Riemer Grootjans的2D射击游戏有趣。 http://www.riemers.net/chi/Tutorials/XNA/Csharp/series2d.php

(他要花一些时间来找到它...... http://www.riemers.net/eng/Tutorials/XNA/Csharp/Series2D/Coll_Detection_Overview.php ......但你可能想跟着一起看他正在解决的问题)

但是我告诫您,Riemers示例不是XNA 4.0,您可能需要做一些工作才能使其运行。但是,这并不困难或神秘。


好的链接,但是旧的链接;我已经将这些用于解决方案。
ashes999 2011年

1
太棒了 我只是想知道有人在搜索时可以找到您的问题,他们将拥有更多资源。
克里斯·戈麦斯

0

我建议创建一个黑白碰撞图。对其进行编程,使黑色像素成为碰撞点。也给角色一个碰撞图;处理地图时,请使用一种算法,该算法会将黑色像素的大方块变成碰撞矩形。将此矩形数据保存在数组中。您可以使用“矩形相交”功能查找碰撞。您还可以使用纹理变换碰撞贴图。

这很像使用碰撞矩阵,但是更高级,您可以对其进行转换!如果需要,可以考虑构建一个碰撞图生成器工具。如果可以使用,请与他人共享代码!

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.