Is there a well-known way (or perhaps reusable bit of code) for pixel-perfect collision detection in XNA?
I assume this would also use polygons (boxes/triangles/circles) for a first-pass, quick-test for collisions, and if that test indicated a collision, it would then search for a per-pixel collision.
This can be complicated, because we have to account for scale, rotation, and transparency.
WARNING: If you're using the sample code from the link from the answer below, be aware that the scaling of the matrix is commented out for good reason. You don't need to uncomment it out to get scaling to work.
Answer
I see that you tagged the question as 2d, so I'll go ahead and dump my code:
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);
}
}
Edit: While this code is almost self explanatory, I did feel bad for not having comments, so I added some ;) I also got rid of the bitwise operations since it was doing basically what the Color.A property does in a more complicated way ;)
No comments:
Post a Comment