Well if you mean 2D sprites and want pixel perfect collision detection then this is my code;
public bool IsTouching(IScratchSprite sprite1, IScratchSprite sprite2)
{
if (sprite1.Visible && sprite2.Visible)
{
if (((ScratchEffects)sprite1.Effects).CheckTexture(sprite1.CurrentCostumeIndex) || ((ScratchEffects)sprite2.Effects).CheckTexture(sprite2.CurrentCostumeIndex))
{
UpdateSpriteEffectTextures();// may be a different costume so need to update now to get correct bounding rects
}
CostumeResource sprite1costume = (CostumeResource)sprite1.Costumes.GetCostume(sprite1.CurrentCostumeIndex);
Matrix sprite1transform = GetSpriteTransform(sprite1, sprite1costume);
Rectangle boundingRect1 = CalculateBoundingRectangle(new Rectangle(0, 0, sprite1costume.texture.Width, sprite1costume.texture.Height), sprite1transform);
CostumeResource sprite2costume = (CostumeResource)sprite2.Costumes.GetCostume(sprite2.CurrentCostumeIndex);
Matrix sprite2transform = GetSpriteTransform(sprite2, sprite2costume);
Rectangle boundingRect2 = CalculateBoundingRectangle(new Rectangle(0, 0, sprite2costume.texture.Width, sprite2costume.texture.Height), sprite2transform);
Rectangle intersection = Rectangle.Intersect(boundingRect1, boundingRect2);
if (!intersection.IsEmpty)
{
Rectangle sprite1intersection = IntersectionOnSprite(intersection, new Rectangle(0, 0, sprite1costume.texture.Width, sprite1costume.texture.Height), sprite1transform);
if (sprite1intersection.IsEmpty)
return false;
UpdateSpriteEffectTextures();
Color[] texture1Data = new Color[sprite1costume.texture.Width * sprite1costume.texture.Height];
ScratchEffects se1 = (ScratchEffects)sprite1.Effects;
se1.GetEffectTexture(sprite1costume).GetData(texture1Data);
Color[] texture2Data = new Color[sprite2costume.texture.Width * sprite2costume.texture.Height];
ScratchEffects se2 = (ScratchEffects)sprite2.Effects;
se2.GetEffectTexture(sprite2costume).GetData(texture2Data);
return IntersectPixels(sprite1transform, sprite1costume.texture.Width, sprite1costume.texture.Height, texture1Data, sprite1intersection,
sprite2transform, sprite2costume.texture.Width, sprite2costume.texture.Height, texture2Data);
}
return false;
}
return false;
}
Rectangle IntersectionOnSprite(Rectangle intersection, Rectangle bounds, Matrix transform)
{
Matrix revert = Matrix.Invert(transform);// convert back to sprite coords
Rectangle intersect2 = CalculateBoundingRectangle(intersection, revert);
intersect2 = Rectangle.Intersect(bounds,intersect2);// clip to sprite bounds
return intersect2;
}
public Matrix GetSpriteTransform(IScratchSprite sprite, CostumeResource spritecostume)
{
Matrix SpriteTransform;
switch (sprite.RotationStyleType)
{
case RotationType.All_Around:
SpriteTransform =
Matrix.CreateTranslation(new Vector3(-spritecostume.ScratchRotationCentreX, -spritecostume.ScratchRotationCentreY, 0.0f)) *
Matrix.CreateScale(sprite.Scale) *
Matrix.CreateRotationZ(sprite.Direction * (float)Math.PI / 180f - (float)Math.PI / 2f) *
Matrix.CreateTranslation(new Vector3(sprite.ScratchX + 240f, -sprite.ScratchY + 180f, 0.0f));
break;
case RotationType.Dont_Rotate:
SpriteTransform =
Matrix.CreateTranslation(new Vector3(-spritecostume.ScratchRotationCentreX, -spritecostume.ScratchRotationCentreY, 0.0f)) *
Matrix.CreateScale(sprite.Scale) *
Matrix.CreateTranslation(new Vector3(sprite.ScratchX + 240f, -sprite.ScratchY + 180f, 0.0f));
break;
case RotationType.Left_Right:
if (sprite.Direction % 180 >= 0)
{
SpriteTransform =
Matrix.CreateTranslation(new Vector3(-spritecostume.ScratchRotationCentreX, -spritecostume.ScratchRotationCentreY, 0.0f)) *
Matrix.CreateScale(sprite.Scale) *
Matrix.CreateTranslation(new Vector3(sprite.ScratchX + 240f, -sprite.ScratchY + 180f, 0.0f));
}
else
{
SpriteTransform =
new Matrix(-1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1) *
Matrix.CreateTranslation(new Vector3(spritecostume.ScratchRotationCentreX, -spritecostume.ScratchRotationCentreY, 0.0f)) *
Matrix.CreateScale(sprite.Scale) *
Matrix.CreateTranslation(new Vector3(sprite.ScratchX + 240f, -sprite.ScratchY + 180f, 0.0f));
}
break;
default:
SpriteTransform = Matrix.Identity;
break;
}
return SpriteTransform;
}
public Rectangle CalculateBoundingRectangle(Rectangle rectangle, Matrix transform)
{
// Get all four corners in local space
Vector2 leftTop = new Vector2(rectangle.Left, rectangle.Top);
Vector2 rightTop = new Vector2(rectangle.Right, rectangle.Top);
Vector2 leftBottom = new Vector2(rectangle.Left, rectangle.Bottom);
Vector2 rightBottom = new Vector2(rectangle.Right, rectangle.Bottom);
// Transform all four corners into work space
Vector2.Transform(ref leftTop, ref transform, out leftTop);
Vector2.Transform(ref rightTop, ref transform, out rightTop);
Vector2.Transform(ref leftBottom, ref transform, out leftBottom);
Vector2.Transform(ref rightBottom, ref transform, out rightBottom);
// Find the minimum and maximum extents of the rectangle in world space
Vector2 min = Vector2.Min(Vector2.Min(leftTop, rightTop),
Vector2.Min(leftBottom, rightBottom));
Vector2 max = Vector2.Max(Vector2.Max(leftTop, rightTop),
Vector2.Max(leftBottom, rightBottom));
// Return as a rectangle
return new Rectangle((int)min.X, (int)min.Y,
(int)(max.X - min.X), (int)(max.Y - min.Y));
}
bool IntersectPixels(
Matrix transformA, int widthA, int heightA, Color[] dataA,
Rectangle intersection,
Matrix transformB, int widthB, int heightB, Color[] dataB)
{
// Calculate a matrix which transforms from A's local space into
// world space and then into B's local space
Matrix transformAToB = transformA * Matrix.Invert(transformB);
// When a point moves in A's local space, it moves in B's local space with a
// fixed direction and distance proportional to the movement in A.
// This algorithm steps through A one pixel at a time along A's X and Y axes
// Calculate the analogous steps in B:
Vector2 stepX = Vector2.TransformNormal(Vector2.UnitX, transformAToB);
Vector2 stepY = Vector2.TransformNormal(Vector2.UnitY, transformAToB);
// Calculate the top left corner of A in B's local space
// This variable will be reused to keep track of the start of each row
Vector2 yPosInB = Vector2.Transform(new Vector2(intersection.Left,intersection.Top), transformAToB);
// For each row of pixels in A
for (int yA = intersection.Top; yA < intersection.Bottom; yA++)
{
// Start at the beginning of the row
Vector2 posInB = yPosInB;
// For each pixel in this row
for (int xA = intersection.Left; xA < intersection.Right; xA++)
{
Color colorA = dataA[xA + yA * widthA];
if (colorA.A != 0)
{
// Round to the nearest pixel
int xB = (int)Math.Round(posInB.X);
int yB = (int)Math.Round(posInB.Y);
// If the pixel lies within the bounds of B
if (0 <= xB && xB < widthB &&
0 <= yB && yB < heightB)
{
// Get the colors of the overlapping pixels
Color colorB = dataB[xB + yB * widthB];
// If both pixels are not completely transparent,
if (colorB.A != 0)
{
// then an intersection has been found
return true;
}
}
}
// Move to the next pixel in the row
posInB += stepX;
}
// Move to the next row
yPosInB += stepY;
}
// No intersection found
return false;
}
I think that just about covers it! Hopefully you can see that I am comparing 2 sprites and that the sprites can have many costumes (Textures or png’s if you prefer) and also graphical effects can be applied to the textures and also sprites can be scaled and have various rotation styles…