1
\$\begingroup\$

I am using MonoGame 3.6 and Visual Studio 2017.

I have been trying to find a solution online to do per pixel collision detection using an animated sprite that I am drawing from a sprite sheet.

Using the following code which is not working:

The BoundingRectangle and Position on the AnimatedSprite.cs

public Vector2 Position
    {
        get { return position; }
        set
        {
            position = value;
        }
    }

public virtual Rectangle BoundingRectangle
    {
        get
        {
            return new Rectangle(
                (int)Position.X,
                (int)Position.Y,
                Width,
                Height);
        }

    }


public static bool CheckForPixelCollision(AnimatedSprite a, AnimatedSprite b)
    {
        // Get Color data of each Texture
        Color[] dataA = new Color[a.Width * a.Height];
        Color[] dataB = new Color[b.Width * b.Height];

        a.Texture.GetData(0, a.Animations[a.CurrentAnimation].CurrentFrameRect, dataA, 0, a.Width * a.Height);
        b.Texture.GetData(0, b.Animations[b.CurrentAnimation].CurrentFrameRect, dataB, 0, b.Width * b.Height);

        int top = Math.Max(a.BoundingRectangle.Top, b.BoundingRectangle.Top);
        int bottom = Math.Min(a.BoundingRectangle.Bottom, b.BoundingRectangle.Bottom);
        int right = Math.Max(a.BoundingRectangle.Right, b.BoundingRectangle.Right);
        int left = Math.Min(a.BoundingRectangle.Left, b.BoundingRectangle.Left);

        for (var y = top; y < bottom; y++)
        {
            for (var x = left; x < right; x++)
            {
                Color color1 = dataA[(x - a.BoundingRectangle.Left) + (y - a.BoundingRectangle.Top) * a.Width];
                Color color2 = dataB[(x - b.BoundingRectangle.Left) + (y - b.BoundingRectangle.Top) * b.Width];

                if (color1.A != 0 && color2.A != 0)
                    return true;
            }
        }

        return false;
    }

When I run this code, I get OutOfRangeException when it tries to get the color out of the array. I am trying to achieve the following steps:

1) Extract the colors from both AnimatedSprites from their SpriteSheet only for the current frame that is drawing from the Animation

2) Check if any of the pixels in the intersecting rectangle are not transparent and return a collision.

An example of how the code will be called when checking Player movement capabilities:

                var nextPosition = (Sprite.Position + (motion * PlayerSpeedAmplifier));
                var playerTempRectangle = new Rectangle((int)nextPosition.X, (int)nextPosition.Y, PlayerCurrentRectangle.Width, PlayerCurrentRectangle.Height);
foreach (var enemy in listOfEnemies)
                {
                    if (playerTempRectangle.Intersects(enemy.EnemyCurrentRectangle))
                    {
                        if (Helper.CheckForPixelCollision(Sprite, enemy.Sprite))
                        {
                            canMove = false;
                            break;
                        }
                    }
                }

I hope someone can please help. Let me know if you need any additional information.

\$\endgroup\$
1
  • \$\begingroup\$ Keep in mind that per-pixel collision detection is going to be very slow and isn't really needed in most games. You might be putting a lot of time and energy into something that isn't really going to scale well anyway. \$\endgroup\$ Jul 19 '18 at 4:22
1
\$\begingroup\$

I know it's been quite a while but I thought I'd provide my working solution to this problem. I'm providing two methods that I used to test if two frames in a sprite sheet collide using per pixel collision detection. Call the one that takes the destination of sprite a, source rectangle of sprite a, the texture of sprite a, the destination of sprite b, the source rectangle of sprite b and the texture of sprite b. The comments should answer any questions as to what the code is doing.

        /// <summary>
        /// Checks if two sprites in a sprite sheet collide using per pixel collision detection
        /// </summary>
        /// <param name="destinationA">Desstination of sprite A</param>
        /// <param name="sourceA">Nullable source rectabgke of sprite A in the sprite sheet</param>
        /// <param name="textureA">Texture for sprite sheet A</param>
        /// <param name="destinationB">Destination of sprite B</param>
        /// <param name="sourceB">Nullable source rectangle of sprite B in the sprite sheet</param>
        /// <param name="textureB">Texture for sprite sheet B</param>
        /// <returns></returns>
        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);
        }

        /// <summary>
        /// Checks to see if two areas of color data collide
        /// </summary>
        /// <param name="a">Destination rectangle of area A</param>
        /// <param name="textureDataA">Color data for area A</param>
        /// <param name="b">Destination rectangle of area B</param>
        /// <param name="textureDataB">Color data for area B</param>
        /// <returns></returns>
        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;
        }
```
\$\endgroup\$
0
\$\begingroup\$

I found an algorithm that works for my scenario.

https://www.youtube.com/watch?v=YxYRj2njBxE

\$\endgroup\$
1
  • 2
    \$\begingroup\$ Thanks for posting an answer! However this is currently a link only answer. Could you post the content of the video that solved your issue? (You can leave the link to the video in the answer. though.) \$\endgroup\$
    – Vaillancourt
    Jul 18 '18 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.