This post about detecting collision between two rotated and/or scaled sprites where the sprites are part of a sprite sheet. I found lots of examples of detecting entire sprites but not so much for sprite sheets. This is an extension of my previous post about detecting if two sprites in sprite sheets collide.
For this to work you need to create a transformation matrix for each sprite. It is calculated by using the following formula: Matrix.CreateTranslation(new Vector3(-Origin, 0)) * Matrix.CreateScale(Scale) * Matrix.CreateRotationZ(Rotation) * Matrix.CreateTranslation(new Vector3(Position, 0)). Origin is calculated by (SourceRectangle.Width / 2, SourceRectangle.Height / 2).
The collision method takes six parameters: the source rectangle of sprite A, the transformation matrix of sprite A, the sprite sheet of sprite A, the source rectangle of sprite B, the transformation matrix of sprite B and the sprite sheet of sprite B. If a source rectangle is null the entire sprite sheet will be used.
It calls a second method that does the actual collision detection. It takes as parameters the source rectangle of sprite A, the transformation matrix of sprite A, the color data of sprite A, the source rectangle of sprite B, the transformation matrix of sprite B and the color data of sprite B.
public static bool SpriteSheetCollision(
Rectangle? sourceA,
Matrix transformA,
Texture2D textureA,
Rectangle? sourceB,
Matrix transformB,
Texture2D textureB)
{
if (sourceA == null)
{
sourceA = new Rectangle(0, 0, textureA.Width, textureA.Height);
}
Color[] textureDataA = new Color[sourceA.Value.Width * sourceA.Value.Height];
textureA.GetData(0, sourceA, textureDataA, 0, textureDataA.Length);
if (sourceB == null)
{
sourceB = new Rectangle(0, 0, textureB.Width, textureB.Height);
}
Color[] textureDataB = new Color[sourceB.Value.Width * sourceB.Value.Height];
textureB.GetData(0, sourceB, textureDataB, 0, textureDataB.Length);
return ColorDataCollides(sourceA.Value, transformA, textureDataA, sourceB.Value, transformB, textureDataB);
}
private static bool ColorDataCollides(Rectangle sourceA, Matrix transformA, Color[] textureDataA, Rectangle sourceB, Matrix transformB, Color[] textureDataB)
{
Matrix mat1to2 = transformA * Matrix.Invert(transformB);
for (int x1 = 0; x1 < sourceA.Width; x1++)
{
for (int y1 = 0; y1 < sourceA.Height; y1++)
{
Vector2 pos1 = new Vector2(x1, y1);
Vector2 pos2 = Vector2.Transform(pos1, mat1to2);
int x2 = (int)Math.Round(pos2.X);
int y2 = (int)Math.Round(pos2.Y);
if ((x2 >= 0) && (x2 < sourceB.Width))
{
if ((y2 >= 0) && (y2 < sourceB.Height))
{
if (textureDataA[x1 + y1 * sourceA.Width].A > 0)
{
if (textureDataB[x2 + y2 * sourceB.Width].A > 0)
{
return true;
}
}
}
}
}
}
return false;
}
The first method is similar to the previous method I posted the other day. It takes transformation matrices instead of destination rectangles. It uses the GetData method overload that takes a source rectangle. For starting position you need to use 0. If your texture has multiple levels you will want to repeat the testing for the different levels.
The second method does the actual collision detection. It calculates a matrix that is used to map the pixels in sprite A to the pixels in sprite B. That is done by multiplying the transformation matrix of sprite A by the inverse transformation of sprite B. I suggest reading up on matrix transformation if you're unclear about what is going on.
Next we loop over all of the pixels in sprite A and test if the collide with a pixel in sprite B. Inside the loops I create a vector for the current pixel in sprite A, or 1 in the code. I transform that vector next using the matrix we calculated earlier. I round the points to integers.
The next step is to check if the transformed pixel is inside the bounds of sprite B. If it is I compare the alpha channel of the two pixels. If their alpha channel are both greater than zero there is a collision and I return true. If a collision is not found I return false.
If you have questions leave a comment and I will answer them. I hope that you find the methods useful.