XNA Rotated PerPixel Collision
Tuesday, January 05 2010 - xna
Yesterday I posted a quick post on a small project I was playing with (Link), and today I saw a post on the XNA Forums where a user was having problems with rotated rectangles and collision. So to help here is the ruff code that i have been playing with, hopefully it will help some one with their project, ATM it is not really complete and i am not fully happy with it. Currently the system slows down the game when I am doing the check on over 300 objects.
A lot of this code is based on the Samples from the Creators Site.
Code for Rotated PerPixel Collision.
using System;using System.Collections.Generic;using System.Linq;using System.Text;using Microsoft.Xna.Framework;using Microsoft.Xna.Framework.Graphics;namespace TranslatedSpriteCollisions{public class GameObject{private Texture2D texture;private Color[] colorData;private Matrix transform;public Vector2 Position;public Vector2 Origin;public Vector2 Velocity;public float Rotation;public float Scale = 1.0f;public Texture2D Texture{get { return texture; }set{texture = value;colorData = new Color[texture.Width * texture.Height];texture.GetData(colorData);this.Origin = new Vector2(texture.Width / 2, texture.Height / 2);}}public Matrix Transform{get { return transform; }}public void UpdateTransform(){transform = Matrix.CreateTranslation(new Vector3(-Origin, 0.0f)) *Matrix.CreateScale(Scale) *Matrix.CreateRotationZ(Rotation) *Matrix.CreateTranslation(new Vector3(Position, 0.0f));}public void Draw(SpriteBatch spriteBatch){spriteBatch.Draw(Texture, Position, null, Color.White, Rotation, Origin,Scale, SpriteEffects.None, 0.0f);}public bool IntersectPixels(GameObject b){return IntersectPixels(transform, texture.Width, texture.Height, colorData,b.transform, b.texture.Width, b.texture.Height, b.colorData);}/// <summary>/// Determines if there is overlap of the non-transparent pixels between two/// sprites./// </summary>/// <param name="transformA">World transform of the first sprite.</param>/// <param name="widthA">Width of the first sprite's texture.</param>/// <param name="heightA">Height of the first sprite's texture.</param>/// <param name="dataA">Pixel color data of the first sprite.</param>/// <param name="transformB">World transform of the second sprite.</param>/// <param name="widthB">Width of the second sprite's texture.</param>/// <param name="heightB">Height of the second sprite's texture.</param>/// <param name="dataB">Pixel color data of the second sprite.</param>/// <returns>True if non-transparent pixels overlap; false otherwise</returns>public static bool IntersectPixelsSlow(Matrix transformA, int widthA, int heightA, Color[] dataA,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 spaceMatrix transformAToB = transformA * Matrix.Invert(transformB);// For each row of pixels in Afor (int yA = 0; yA < heightA; yA++){// For each pixel in this rowfor (int xA = 0; xA < widthA; xA++){// Calculate this pixel's location in BVector2 positionInB =Vector2.Transform(new Vector2(xA, yA), transformAToB);// Round to the nearest pixelint xB = (int)Math.Round(positionInB.X);int yB = (int)Math.Round(positionInB.Y);// If the pixel lies within the bounds of Bif (0 <= xB && xB < widthB &&0 <= yB && yB < heightB){// Get the colors of the overlapping pixelsColor colorA = dataA[xA + yA * widthA];Color colorB = dataB[xB + yB * widthB];// If both pixels are not completely transparent,if (colorA.A != 0 && colorB.A != 0){// then an intersection has been foundreturn true;}}}}// No intersection foundreturn false;}/// <summary>/// Determines if there is overlap of the non-transparent pixels between two/// sprites./// </summary>/// <param name="transformA">World transform of the first sprite.</param>/// <param name="widthA">Width of the first sprite's texture.</param>/// <param name="heightA">Height of the first sprite's texture.</param>/// <param name="dataA">Pixel color data of the first sprite.</param>/// <param name="transformB">World transform of the second sprite.</param>/// <param name="widthB">Width of the second sprite's texture.</param>/// <param name="heightB">Height of the second sprite's texture.</param>/// <param name="dataB">Pixel color data of the second sprite.</param>/// <returns>True if non-transparent pixels overlap; false otherwise</returns>public static bool IntersectPixels(Matrix transformA, int widthA, int heightA, Color[] dataA,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 spaceMatrix 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 rowVector2 yPosInB = Vector2.Transform(Vector2.Zero, transformAToB);// For each row of pixels in Afor (int yA = 0; yA < heightA; yA++){// Start at the beginning of the rowVector2 posInB = yPosInB;// For each pixel in this rowfor (int xA = 0; xA < widthA; xA++){// Round to the nearest pixelint xB = (int)Math.Round(posInB.X);int yB = (int)Math.Round(posInB.Y);// If the pixel lies within the bounds of Bif (0 <= xB && xB < widthB &&0 <= yB && yB < heightB){// Get the colors of the overlapping pixelsColor colorA = dataA[xA + yA * widthA];Color colorB = dataB[xB + yB * widthB];// If both pixels are not completely transparent,if (colorA.A != 0 && colorB.A != 0){// then an intersection has been foundreturn true;}}// Move to the next pixel in the rowposInB += stepX;}// Move to the next rowyPosInB += stepY;}// No intersection foundreturn false;}}}
Similar Posts
- Phoenix, Adding to the Background
- Phoenix, Drawing Text on the Screen
- Getting Started with Blender 3D and XNA

It's unlikely that you have to test all 300 of those objects at a per-pixel level. Check to see if the rotated bounding rectangles of the objects intersect first. I bet Graphics Gems has an algorithm to do that efficiently.
You could even cut that work down if you code something like "if the centers of the two objects are more than x pixels apart, they can't be overlapping no matter how they're rotated".
Check you skip are checks that can't slow your code.