1
\$\begingroup\$

I have done a function to check the pixels collision against two animated sprites, and now I need to check a "Rectangle x animated sprite" collision, but I have no idea how to do this. I tried to create a new Texture2D for the rectangle and fill it with some color, but it is expensive to do at every frame when I need check the collisions.

This is my function to check the pixel colisions of two animated sprites:

public static bool IntersectCharacterSpritePixels(CharacterSprite spriteA, CharacterSprite spriteB)
{
    if (!spriteA.BoundingBox.Intersects(spriteB.BoundingBox))
    {
        return false;
    }

    // Get the current frame for each sprite
    var rectangleA = spriteA.GetCurrentFrameRectangle();
    var rectangleB = spriteB.GetCurrentFrameRectangle();

    // Create the color data based on the current frame
    Color[] dataA = new Color[rectangleA.Width * rectangleA.Height];
    spriteA.TextureRegion.Texture.GetData(0, new Rectangle(rectangleA.X, rectangleA.Y, rectangleA.Width, rectangleA.Height), dataA, 0, dataA.Length);

    Color[] dataB = new Color[rectangleB.Width * rectangleB.Height];
    spriteB.TextureRegion.Texture.GetData(0, new Rectangle(rectangleB.X, rectangleB.Y, rectangleB.Width, rectangleB.Height), dataB, 0, dataB.Length);

    // Find the bounds of the rectangle intersection
    var boundingA = spriteA.BoundingBox;
    var boundingB = spriteB.BoundingBox;

    int top = Math.Max(boundingA.Y, boundingB.Y);
    int bottom = Math.Min(boundingA.Bottom, boundingB.Bottom);
    int left = Math.Max(boundingA.Left, boundingB.Left);
    int right = Math.Min(boundingA.Right, boundingB.Right);

    // Check every point within the intersectionbounds
    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 = dataA[(x - boundingA.X) + (y - boundingA.Y) * rectangleA.Width];
            Color colorB = dataB[(x - boundingB.X) + (y - boundingB.Y) * rectangleB.Width];

            // Ifboth 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\$
  • \$\begingroup\$ To be honest I don't think this is worth doing. In almost all cases simple shape collision detection will be sufficient and a lot faster. Even if you manage to do it, a simple true or false is not going to be good enough to implement the collision response. \$\endgroup\$ – craftworkgames Feb 9 '16 at 6:20
  • \$\begingroup\$ @craftworkgames I need know if an attack (a rectangle) hits an enemy (a sprite), I already do Rectangle.Intersects, but if the attack hits a transparent area of the sprite the attack is considered and the things does not are so natural. The attack does not touch the sprite but is still considered. \$\endgroup\$ – Rafael Almeida Feb 10 '16 at 0:30
  • \$\begingroup\$ Right, but most games don't go to the pixel level. You can get pretty convincing collisions just but adjusting the size of your rectangle or perhaps use multiple simple shapes to cover the general area of pixels. I'd be interested to know of a game that does pixel level collisions if you can find an example. \$\endgroup\$ – craftworkgames Feb 10 '16 at 3:56
  • \$\begingroup\$ @craftworkgames, it has not been feasible and/or possible until around DX9. DX11's new queries and shader stages blew a lot of the "old ways" out the window. Now, DX12 is out, making full use of every core you throw at it. We can, literally, rewrite some of the rule books, and need to. I like the idea of Unity and appreciate that it makes game programming more accessible, but I think it comes with a downside in that many new games are still XNA games, written in DX11/12. /2cents \$\endgroup\$ – Jon Feb 10 '16 at 19:19
  • \$\begingroup\$ We don't acknowledge that DX between 9 and 11..... :) \$\endgroup\$ – Jon Feb 10 '16 at 19:20
2
\$\begingroup\$

A couple of things:

You should avoid creating, and recreating the Color arrays. Find a way to create them once and avoid GC.

The textures are not generated by the GPU and won't force it to synchronize with the CPU, but you should avoid copying and/or sifting through it on the CPU if at all possible.

As a direct answer to your question, the Rectangle class, which you are already using, has an Intersects() methods. For rectangle vs. sprite, just loop through a sprite's color array like you are now and, if the color at X,Y is not fully transparent, check the rectangle for intersection with X,Y. If the result isn't Disjoint, they collided.


Instead, research using the OcclusionQuery class in conjunction with the stencil buffer. Doing so should allow you to check way more intersections, way faster, and can handle any intersection between anything renderable. The stencil buffer makes sure the GPU only processes the pixels within the intersection (far fewer than the combined CPU BoundingBox). It also processes each of those pixels at the same time in parallel ("instant").

Edit:
An OcclusionQuery design would go something like this:

  1. Disable color writes
    //No need to actually draw anything
  2. Enable alpha testing
    //Fully transparent pixels are omitted
  3. Enable stencil buffer
  4. Set stencil buffer to always pass, incrementing
    //Write 1 to the stencil for every modified pixel
  5. Render the first texture (or filled rectangle)
    //Fill stencil
  6. Set stencil buffer to pass when GREATER than 0
    //Omit pixels where stencil=0
  7. Begin query
    //Start counting the number of colors "written"
  8. Render the second texture
  9. End query
    //Stop counting
  10. [Do other stuff to give the GPU time to work]
  11. Read query result

The result contains the integer number of overlapping, non-transparent, pixels.
Any non-zero value is a collision.

Remember to re-enable color writes.

\$\endgroup\$
  • \$\begingroup\$ Good example and usage: msdn.microsoft.com/en-us/library/… \$\endgroup\$ – Jon Feb 9 '16 at 7:41
  • \$\begingroup\$ Thanks! I will try check the rectangle intersection with the sprites pixels. Is there a way to I create the colors array only one time when importing the entire spritesheet and on the collision time only get the current frame? I really didn't understand the OccusionQuery usage for the current problem, can you explain? \$\endgroup\$ – Rafael Almeida Feb 10 '16 at 0:36
  • \$\begingroup\$ If you are intent on looping through them on the CPU, you just need to make one color array that will hold the entire spritesheet. The array could be 2 dimensional to allow easy X,Y access based on spriteA.GetCurrentFrameRectangle. \$\endgroup\$ – Jon Feb 10 '16 at 1:29
  • \$\begingroup\$ I also added more details about OcclusionQueries \$\endgroup\$ – Jon Feb 10 '16 at 1:51
  • \$\begingroup\$ I tried the to implement the way you said and it worked, thanks. \$\endgroup\$ – Rafael Almeida Feb 10 '16 at 19:46

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.