0
votes

Right now to do rotation I have a separate frame for each angle of rotation, but I much rather have one image that I rotate. My question is, how do I do a matrix transformation in XNA/C# so the color data is also rotated the same way the sprite is rendered on the screen? I found this old SO question, but I really don't understand the answer, and I'd like to know how the code works in case it needs to be modified later.

Any and all positional/rotational information needed is stored in the entity that contains the sprite, and both sprites are assumed to rotate.

Here is my code so far:

private static bool PixelPerfect(PhysicsEntity a, PhysicsEntity b)
    {
        if (!BoundingBox(a, b)) return false;

        var colorDataA = new Color[a.Sprite.Source.Width * a.Sprite.Source.Height];
        var colorDataB = new Color[b.Sprite.Source.Width * b.Sprite.Source.Height];

        a.Sprite.Texture.GetData(0, a.Sprite.Source, colorDataA, 0, colorDataA.Length);
        b.Sprite.Texture.GetData(0, b.Sprite.Source, colorDataB, 0, colorDataB.Length);

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

        for (var y = top; y < bottom; y++)
        {
            for (var x = left; x < right; x++)
            {
                var colorA = colorDataA[(int) ((y - a.BoundingBox.Top) * (a.BoundingBox.Width) + (x - a.BoundingBox.Left))];
                var colorB = colorDataB[(int) ((y - b.BoundingBox.Top) * (a.BoundingBox.Width) + (x - a.BoundingBox.Left))];
                if (colorA.A == 0 || colorB.A == 0) continue;
                a.CollisionPoint.ChangePositon(x, y);
                b.CollisionPoint.ChangePositon(x, y);
                return true;
            }
        }
        return false;
    }
1

1 Answers

0
votes

You should keep the data constant. You just modify the way you access it.

For pixel-perfect collision detection, the basic idea is as follows: You have two sprites that are rendered with transform matrices m1 and m2, respectively. Now iterate every pixel of sprite 1. Find out what screen position this pixel is located at. Find out the pixel of sprite 2 that is located at the same (or similar) position.

The transform matrices are usually something like

t = Matrix.CreateRotationZ(angle) * Matrix.CreateTranslation(position);

You can check your matrix calculation with the overload of SpriteBatch.Begin() that takes a matrix.

The transformation from sprite 1's pixel space to world space is:

worldPos = sprite1LocalPos * m1

The transformation form world space to sprite 2's pixel space is:

sprite2LocalPos = worldPos * Matrix.Invert(m2)
                = sprite1LocalPos * m1 * Matrix.Invert(m2)

And this is basically all you need to do:

Matrix transform = m1 * Matrix.Invert(m2);
Rectangle rect = new Rectangle(0, 0, texture2.Width, texture2.Height);
for (int x = 0; x < texture1.Width; x++)
{
    for (int y = 0; y < texture1.Height; y++)       //Iterate every pixel of sprite 1
    {
        index = x + texture1.Width * y;             //Calculate the pixel index
        if (data1[index].A < 10)                    //If the pixel is (almost) transparent, 
            continue;                               //ignore it

        Vector2 sprite1Local = new Vector2(x, y);
        Vector2 sprite2Local                              //get the pixel of sprite 2 
            = Vector2.Transform(sprite1Local, transform); //that is at the same position

        if(!rect.Contains((int)sprite2Local.X, (int)sprite2Local.Y))
                                                    //if this is outside of the sprite, 
            continue;                               //ignore it

        index2 = (int)sprite2Local.X + (int)sprite2Local.Y * texture2.Width;
        if(data2[index2].A>10)                      //If the pixel is not transparent, 
            return true;                            //there is a collision
    }
}
return false;