Monday, October 26, 2020

Per Pixel Sprite Sheet Collision Detection

 I've seen this question asked a few times but no public answers. How do you detect if two sprites in a sprite sheet collide using per pixel collision detection? The answer for entire sprites is available easily. I made two static methods that can be added to a project to check if two parts of a sprite sheet collide. Soon I will be posting code that handles rotated and scaled sprites as well.



public static bool SpriteSheetCollision(
    Rectangle destinationA, 
    Rectangle? sourceA, 
    Texture2D textureA,
    Rectangle destinationB,
    Rectangle? sourceB,
    Texture2D textureB)
{
    // If the sourceA is null use entire texture
    if (sourceA == null)
    {
        sourceA = new Rectangle(0, 0, textureA.Width, textureA.Height);
    }

    // Grab the texture data for checking if the pixels collide in the source rectangle
    Color[] textureDataA = new Color[sourceA.Value.Width * sourceA.Value.Height];
    textureA.GetData(0, sourceA, textureDataA, 0, textureDataA.Length);

    // If the sourceB is null use the entire texture as the source rectangle
    if (sourceB == null)
    {
        sourceB = new Rectangle(0, 0, textureB.Width, textureB.Height);
    }

    // Grab the texture data for checking if the pixels collide in the source rectangle
    Color[] textureDataB = new Color[sourceB.Value.Width * sourceB.Value.Height];
    textureB.GetData(0, sourceB, textureDataB, 0, textureDataB.Length);

    // Call the per pixel collision detection code
    return ColorDataCollides(destinationA, textureDataA, destinationB, textureDataB);
}

public static bool ColorDataCollides(Rectangle a, Color[] textureDataA, Rectangle b, Color[] textureDataB)
{
    // Find the bounds of the rectangle intersection
    int top = Math.Max(a.Top, b.Top);
    int bottom = Math.Min(a.Bottom, b.Bottom);
    int left = Math.Max(a.Left, b.Left);
    int right = Math.Min(a.Right, b.Right);

    // Check every point within the intersection bounds
    for (int y = top; y < bottom; y++)
    {
        for (int x = left; x < right; x++)
        {
            // Get the color of both pixels at this point
            Color colorA = textureDataA[(x - a.Left) +
                                    (y - a.Top) * a.Width];
            Color colorB = textureDataB[(x - b.Left) +
                                    (y - b.Top) * b.Width];

            // If both pixels are not completely transparent,
            if (colorA.A > 0 && colorB.A > 0)
            {
                // then an intersection has been found
                return true;
            }
        }
    }

    // No intersection found
    return false;
}

No comments:

Post a Comment