 |
|
|
|
Improved HitTest Method for Silverlight 3
|
|
|
 |
|
Location: Blogs Andy's Blog |
|
| Posted by: host |
7/10/2009 8:24 AM |
With the new Silverlight 3 WriteableBitmap class, it’s possible to do pixel-perfect collision detection (a “HitTest”) on bitmap (raster) images. In a previous blog post, I showed how we can do a HitTest on Vector graphics in Silverlight 2. In this blog post, we’ll enhance the collision detection to handle BOTH Vector graphics and Raster graphics in Silverlight 3.
VIEW THE DEMO
DOWNLOAD THE SOURCE
Looking at the demo code, one of the main routines is the CheckCollisionPoint class, which does a HitTest on a UI Element at a specific Point. If the element is an Image, then a WriteableBitmap object is used to check the specific pixel point (this WriteableBitmap is created in the CheckCollision method, shown later.). If the element is not an image, then we assume it is Vector based and we use the VisualTreeHelper class for a hit test:
public bool CheckCollisionPoint(Point pt, FrameworkElement control, FrameworkElement controlElem)
{
if (controlElem is Image)
{
// NOTE that we saved the WB in the Tag object for performance.
// in a real app, you would abstract this in your sprite class.
WriteableBitmap wb = controlElem.Tag as WriteableBitmap;
int width = wb.PixelWidth;
int height = wb.PixelHeight;
double offSetX = Convert.ToDouble(control.GetValue(Canvas.LeftProperty));
double offSetY = Convert.ToDouble(control.GetValue(Canvas.TopProperty));
pt.X = pt.X - offSetX;
pt.Y = pt.Y - offSetY;
int offset = Convert.ToInt32((width * pt.Y) + pt.X);
return (wb.Pixels[offset] != 0);
}
else
{
List<UIElement> hits = System.Windows.Media.VisualTreeHelper.FindElementsInHostCoordinates(pt, controlElem) as List<UIElement>;
return (hits.Contains(controlElem));
}
} |
The CheckCollisionPoint method is used by the CheckCollision method, which checks for a collision between two UI elements. Note that we always want to do a quick Rect.Intersect test on the bounds of the elements before doing the slower per-pixel Hit Test. This method also creates the WriteableBitmap and stores it in the Tag property (you may want to abstract this into a Sprite class in a real application):
private bool CheckCollision(FrameworkElement control1, FrameworkElement controlElem1, FrameworkElement control2, FrameworkElement controlElem2)
{
// first see if sprite rectangles collide
Rect rect1 = UserControlBounds(control1);
Rect rect2 = UserControlBounds(control2);
rect1.Intersect(rect2);
if (rect1 == Rect.Empty)
{
// no collision - GET OUT!
return false;
}
else
{
bool bCollision = false;
Point ptCheck = new Point();
// NOTE that creating the writeablebitmap is a bit intense
// so we will do this once and store results in Tag property
// in a real game, you might abstract this into a Sprite class.
if (controlElem1 is Image)
controlElem1.Tag = GetWriteableBitmap(control1);
if (controlElem2 is Image)
controlElem2.Tag = GetWriteableBitmap(control2);
// now we do a more accurate pixel hit test
for (int x = Convert.ToInt32(rect1.X); x < Convert.ToInt32(rect1.X + rect1.Width); x++)
{
for (int y = Convert.ToInt32(rect1.Y); y < Convert.ToInt32(rect1.Y + rect1.Height); y++)
{
ptCheck.X = x;
ptCheck.Y = y;
if (CheckCollisionPoint(ptCheck, control1, controlElem1))
if (CheckCollisionPoint(ptCheck, control2, controlElem2))
{
bCollision = true;
break;
}
}
if (bCollision) break;
}
return bCollision;
}
} |
A quick note about performance: This method provides a simple way of getting pixel-perfect collision detection in Silverlight 3. But if you have a lot of elements that are colliding simultaneously (without being destroyed), this may not perform well. Also, high velocity objects could pass through each other.
|
|
| Permalink |
Trackback |
Comments (10)
Add Comment
|
andy.beaulieu.com > Home - Improved HitTest Method for Silverlight 3
|
By TrackBack on
7/11/2009 8:00 AM
|
Thank you for submitting this cool story - Trackback from NewsPeeps # NewsPeeps
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
7/12/2009 1:44 PM
|
|
Good Work, that goes for You and the SL team.
|
|
|
Merry X-Mas Silverlight 3
|
By TrackBack on
12/24/2009 12:40 PM
|
Merry X-Mas Silverlight 3 # Wesly van Zoen
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
1/23/2010 3:42 AM
|
|
Thanks a lot man .... amazing work you have here. Thanks for sharing.
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
2/18/2010 5:01 AM
|
|
Your function UserControlBounds in the sample code doesn't work correctly if an object is in a grid and/or uses automatic widths and heights. Setting it to use ActualWidth and ActualHeight will fix part of the problem, but getting the topLeft coordinate is proving a bit trickier. I've tried using control.TransformToVisual(Application.Current.RootVisual).Transform(new Point()); but that throws an exception of "Value does not fall within the expected range." Any suggestions?
|
|
|
Merry X-Mas Silverlight 3
|
By TrackBack on
2/22/2010 11:28 AM
|
Merry X-Mas Silverlight 3 # www.wez.nl
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
6/7/2010 12:20 PM
|
Hey Andy, I made a video tutorial on your HitTest technique: http://tinyurl.com/2bgbp7s
Thanks very much this is an awesome technique!
Victor Gaudioso
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
7/28/2010 9:04 PM
|
Hello and thank for your post. I was wanting to use something like this, but I'm using keydown for my movement. How would I incorporate this so I couldn't move in a direction if there's a collision?
Thanks in advance Logan
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
2/28/2011 2:53 PM
|
Thanks very much. I created a game using your technique, is a simple arcade shooter.
Greetings!
|
|
|
Re: Improved HitTest Method for Silverlight 3
|
By Anonymous on
2/28/2011 2:56 PM
|
I'm sorry, I forgot to put the link of the game:) is this
http://www.mashooo.com/SilverlightGames/Rapid_Destroyer.aspx
Greetings!
|
|
|
|
 |
|
 |
|
|