Using BitmapData.hitTest for Collision Detection
The Flash Player contains a number of APIs for handling collision detection within Flash content. The DisplayObject class contains hitTest and hitTestPoint which can be useful if you need to detect bounding box collisions, or detect collisions between an individual point and bounding boxes or shapes.
However, BitmapData also contains a hitTest API, which can check collisions on BitmapData. Where the API really shines, is when you need to detect collisions between the visible areas of DisplayObjects (and not just of their bounding boxes). The API contains functionality for testing collisions between BitmapData and a Point, BitmapData and a Rectangle, and BitmapData and another BitmapData. It is the last item that I will focus on in this post.
Since you can get BitmapData from a DisplayObject the API can be used to detect very exact collisions between the visible areas of DisplayObjects, even if the DisplayObjects have irregular shapes.
Here is a simple example that uses the API to detect collisions between the visible areas of two MovieClips using BitmapData.hitTest.
And here is the code
var redRect:Rectangle = redClip.getBounds(this);
var redClipBmpData = new BitmapData(redRect.width, redRect.height, true, 0);
redClipBmpData.draw(redClip);
var blueRect:Rectangle = blueClip.getBounds(this);
var blueClipBmpData = new BitmapData(blueRect.width, blueRect.height, true, 0);
blueClipBmpData.draw(blueClip);
addEventListener(Event.ENTER_FRAME, enterFrame);
function enterFrame(e:Event):void
{
blueClip.x = mouseX;
blueClip.y = mouseY;
if(redClipBmpData.hitTest(new Point(redClip.x, redClip.y),
255,
blueClipBmpData,
new Point(blueClip.x, blueClip.y),
255
))
{
trace("hit");
redClip.filters = [new GlowFilter()];
}
else
{
redClip.filters = [];
}
}
Basically, the content contains two MovieClips on the timeline. We draw the graphics for each MovieClip into a BitmapData instance, and then use the BitmapData.hitTest API to see if the visible parts of the MovieClip (non-transparent) instances collide.
Couple of notes on the example:
1. The initial fill for the BitmapData is transparent pixels. This is necessary to detect just the visible areas of the DisplayObject.
2. The Point instances passed to hitTest are used to align the BitmapData instances for the comparisons.
This example works regardless of the contents or shape of the DisplayObjects. However, if either of the DisplayObjects have had any transformations applied to them (such as rotation), then the collision detection wont work correctly.
In order to get it to work, you need to apply a Matrix when drawing the BitmapData to account for the transformation applied to one or both DisplayObjects. If you haven’t worked with Matrices and DisplayObject transformations, this can be a little daunting at first (it was for me). If you are new to using Matrices, I suggest reading this excellent article on Matrices in Flash over at senocular.com.
Luckily for me, Trevor McCauley, who runs senocular.com, also happens to works for Adobe on the Flash Player team. He really helped me understand how how to get the hit detection to work when the DisplayObjects have had transformations applied to them.
Here is the example:
and the code:
addEventListener(Event.ENTER_FRAME, enterFrame);
function enterFrame(e:Event):void
{
blueClip.x = mouseX;
blueClip.y = mouseY;
redClip.rotation++;
var blueRect:Rectangle = blueClip.getBounds(this);
var blueOffset:Matrix = blueClip.transform.matrix;
blueOffset.tx = blueClip.x - blueRect.x;
blueOffset.ty = blueClip.y - blueRect.y;
var blueClipBmpData = new BitmapData(blueRect.width, blueRect.height, true, 0);
blueClipBmpData.draw(blueClip, blueOffset);
var redRect:Rectangle = redClip.getBounds(this);
var redClipBmpData = new BitmapData(redRect.width, redRect.height, true, 0);
var redOffset:Matrix = redClip.transform.matrix;
redOffset.tx = redClip.x - redRect.x;
redOffset.ty = redClip.y - redRect.y;
redClipBmpData.draw(redClip, redOffset);
var rLoc:Point = new Point(redRect.x, redRect.y);
var bLoc:Point = new Point(blueRect.x, blueRect.y);
if(redClipBmpData.hitTest(rLoc,
255,
blueClipBmpData,
bLoc,
255
))
{
trace("hit");
redClip.filters = [new GlowFilter()];
}
else
{
redClip.filters = [];
}
blueClipBmpData.dispose();
redClipBmpData.dispose();
}
Note that we use the bounds of each DisplayObject to determine the BitmapData size and not the height / width of the DisplayObject. This is because the transformation will most likely modify the bounds, and we need to take this into account. In the first example, we could have just used the height and width of the DisplayObjects since they were the same as the bounds, but in general you should get in the habit of using the bounds.
We also pass a Matrix to each BitmapData.draw call to account for any transformations that may have been applied to the DisplayObjects (in this case rotations). I could try and explain what the Matrix is doing, but to be honest, im still trying to get my head around it. Instead, Ill let Trevor explain it (from an email he sent to me):
For this you need the transformation of the object as defined by DisplayObject.transform.matrix. Since everything is in the same container, we still won’t have to worry about walking up hierarchies to get contcatenated/global matrices – we can just use that which is directly applied to the target object (transform.matrix). This gives you the full transform of that object. But for the purposes of capturing a bitmap, we only want the non-translation parts of that transform. This is because when a bitmap is captured, it’s captured from the 0,0 location in the coordinate space of the captured object outward into positive space. The translation of the matrix of the desired object is its position in its parent coordinate space, not its own. HOWEVER, if that object’s own 0,0 location is within the middle of its graphic elements, drawing it into a Bitmap directly from 0,0 could cause cropping. So in terms of that translation as it is to be used with BitmapData.draw(), it’s still important to make sure everything in the target object is captured. And in doing that, it means shifting everything that would appear in negative coordinate space over into positive coordinate space.
Luckily, using getBounds (again) this is quite simple. We can use getBounds to determine the bounds of an object in its parent coordinate space – this includes its transformations. We also know the location of 0,0 within the parent coordinate space because that is the same location it places the target object using its x and y properties. With that, we can get the needed bitmap translation by simply subtracting the bounds x,y from the object’s own x,y. In code that essentially becomes
var b = t.getBounds(t.parent);
var m = t.transform.matrix;
m.tx = t.x – b.x;
m.ty = t.y – b.y;
var bmp = new BitmapData(b.width, b.height, true, 0);
bmp.draw(t, m);where ‘t’ is the target display object.
Now, I will be the first to admit that I am still wrapping my head around some of this stuff, especially with the use of the Matrices to offset the transformation. However, the second example above is generic and robust enough to be used for general pixel perfect collision detection on visible portions of DisplayObjects.
One note, is that the examples above assumes that the items being compared share the same parent. If they don’t you need to make some additional adjustments (to take into consideration other transformations), but that is a post for another day.
Btw, if you don’t already, you need to read senocular.com. This is one of the best resources on understanding ActionScript 3 and the Flash Player.
Update : One optimzation which you can make is to first check if the bounding boxes of the DisplayObjects overlap (using DisplayObject.hitTestObject), and only if they do, use BitmapData.hitTest to see if their visible areas are touching.

Then there is the very cool Collision Detection Kit:
http://coreyoneil.com/portfolio/index.php?project=5
Clint
24 Jun 09 at 9:26 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
@Clint
Yes. Collision detection kit is really nice, and adds a lot of functionality (such as finding the angle of the collision).
I was actually using it in my project, but switch to this, as in my case, I can re-use / cache the bitmap data, which gives me a significant performance boost.
mike chambers
mesh@adobe.com
mikechambers
24 Jun 09 at 10:02 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Thanks – this is cool.
This should be built into the player, right?
Iain
25 Jun 09 at 1:15 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
it’s cool. nice tip.
i will always thank you for your consideration.
kenlaszlo
25 Jun 09 at 1:44 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] bitmapdata collision detection http://www.mikechambers.com/blog/2009/06/24/using-bitmapdata-hittest-for-collision-detection/ [...]
25/06/2009 « Robertopriz’s Weblog
25 Jun 09 at 6:13 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Thanks for this valuable guide. It’s something that people have been spending time and effort to figure out for themselves since Flash 8 came out.
I do have to say that all this clearly represents the intent of those who designed the API, and therefore should have been incuded in the documentation from the start.
Here’s another example: the effect of the mergeAlpha parameter of copyPixels(). Moock states in EAS 3.0 that it uses the SCREEN blending mode, and gives the precise formula. Why don’t the docs mention this?
And of course the documentation on the DisplacementMapFilter — don’t get me started…
Alan Shaw
25 Jun 09 at 9:58 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] you have read my blog any this week, you have probably noticed that I have been doing some basic research on collision detection [...]
Relative performance for collision detection techniques in ActionScript 3 at Mike Chambers
26 Jun 09 at 3:28 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
var performanceBoost = new Refactor();
if(isCDKOpenSource){
CDK.read();
CDK.edit(performanceBoost);
CDK.publish();
}
=D
SparK
20 Jul 09 at 8:36 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
This works great with clips that are aligned top-left but when I used center aligned clips only the center point detects as collision. I thought “blueClip.x – blueClip.width/2 ” would be the correct point for a centered clip but it isn’t working as expected. Why is that?
brad
29 Jul 09 at 10:45 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Thanks, this is just what I had been looking for. It works with my own objects that change position, too.
hannah
12 Aug 09 at 9:20 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Brad, use the second example Mike posted. It uses the transform matrix to counter any offset created from a registration point not in the top left, as well as counter any rotation or scaling. The part that does this is:
var blueOffset:Matrix = blueClip.transform.matrix;
blueOffset.tx = blueClip.x – blueRect.x;
blueOffset.ty = blueClip.y – blueRect.y;
blueClipBmpData.draw(blueClip, blueOffset);
And same for the redClip.
Aaron
12 Aug 09 at 12:55 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] I made a post earlier showing how (with much, much help from Senocular), I was able to use Matrix to do hit tests using BitmapData.hitTest on DisplayObjects which have had transformations applied to them (in this case, [...]
Converting from a Matrix3D to Matrix in ActionScript 3 at Mike Chambers
9 Sep 09 at 10:48 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Thanks very much for posting this Mike.
I’m pretty new to AS3 and I’ve spent weeks investigating the most effective way to implement hit testing in the platform game I’m developing.
I’ve not found a conclusion this comprehensive anywhere, and it all makes total sense.
Claire
9 Feb 10 at 7:15 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
What if the two movieclips you wish to compare are composed of many nested clips, all of which could be at any arbitrary x/y value and any arbitrary scale? It seems like the matrix transformation trick works if only the first layer is off center or rotated, but if the clip is many layers deep and the transformations are completely random for each layer, it’s not working.
For instance, what if blueClip were like this:
blueClip x=20, y=50
+ blueClipChild x = 400, y = -20
+ blueClipChildChild x = -1309, y = 20
+ Shape (square, 30px/300px at x = 10, y = -280)
How would you solve for this?
anon
15 Feb 10 at 2:32 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
after few days lookin for right approach to object with scale and rotation involved with hitTestObject issue, i am full of respect for the author of this piece of code.
On hundreds of sites i can find how to make accurate hitTest against static objects, but only here with so small piece of code i found totally amazing and fast workaround. Great stuff! Thank you.
northmantif
17 Mar 10 at 7:10 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
I can’t seem to get the first example working…
I’ve made the 2 movie Clips, Using PolyStar and Oval Tools, But I can’t seem to get them working
Erro 1046: Type was not found or was not a compile-time constant: redClip and same for blueClip :(
David
8 May 10 at 9:51 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] This code was adapted from a post by Mike Chambers [...]
Pixel perfect hit detection in Actionscript 3 | Engine Digital Inc. / Blog
14 Jun 10 at 12:47 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
BitmapData.hittest still breaks when applying a rotation over a specific axis. Especially the X & Y.
the transform.matrix will be removed and a transform.matrix3D will be created…..
Any fix for this ?
Rackdoll
30 Aug 10 at 5:18 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Great article! it really helped me.
To give something back I’m going to explain how I solved this problem if, for example, the redClip has a parent different than “this”.
First we get it’s position relative to “this”:
var pRedPosition:Point = new Point(redClip.x, redClip.y);
pRedPosition= redClip.parent.localToGlobal(pRedPosition);
pRedPosition= this.globalToLocal(pRedPosition);
Than we replace these lines:
redOffset.tx = redClip.x – redRect.x;
redOffset.ty = redClip.y – redRect.y;
with:
redOffset.tx = pRedPosition.x – redRect.x;
redOffset.ty = pRedPosition.y – redRect.y;
If blueClip has a different parent than “this” too we do something similar.
AdyR
30 Oct 10 at 8:18 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
@Rackdoll
What you can do is to derive a 2D matrix from the 3D matrix by discarding the Z data.
Then you will have an extra step to check for this depth data yourself (when you know that in 2D they collide)
private static function getMatrix(obj:DisplayObject, boundX:Number, boundY:Number):Matrix {
if (obj.transform.matrix != null)
return obj.transform.matrix.clone();
var objM:Matrix3D = obj.transform.matrix3D;
var rawMatrixData:Vector. = objM.rawData;
var matrix:Matrix = new Matrix();
matrix.a = rawMatrixData[0];
matrix.c = rawMatrixData[1];
matrix.tx = obj.x – boundX;
matrix.b = rawMatrixData[4];
matrix.d = rawMatrixData[5];
matrix.ty = obj.y – boundY;
return matrix;
}
you pass a DisplayObject and its bounding x and y coordinates.
LoL
4 Nov 10 at 4:20 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Hi,
I like this piece of code very much but when one of the movieclip’s registration point is changed to center it won’t work perfectly.
I need this code to be updated for regardless of any registration of the movieclip.
I tried a lot of I could not:(
please can any one help me???
I need my one movieclip in center registration point.
plz plz help!!!
help seeker
18 Jan 11 at 2:14 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] du suchst ist BitmapData.hitTest() Using BitmapData.hitTest for Collision Detection at Mike Chambers mfg [...]
hitTestObject nicht auf die Bounding-Box sondern auf Inhalt anwenden? - Flashforum
27 May 11 at 9:15 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
[...] Må skynde mig at sige at jeg har stjålet den grundlæggende kode for følgende, herfra. [...]
AS3 Pixel Perfect Collision/hittest | Krüger's Blog
30 May 11 at 2:22 am edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>
Hey Guys,
Not sure if this forum is still used, but just wanted to say hi and ask a question.
Definitely, props to the creator of this code. My question though, is that I’ve been building a small adventure like game where a character travels around a small town and this code works great for that sort of set up. I had to change it up a little bit, meaning instead of using the filter I just changed it to “colliding = true” or “false” depending on if the character is hitting the Bitmap image area.
The problem I’m having though is that when my character collides with one of the walls he bounces. This is if I have the collision calling with the code for when the moving button is pushed down. If I don’t add the code there then the character simply just sticks to the wall. I added a code to make the character shoot back from the wall when it’s hit but like I said that either causes the bounce, or the character sticks to the wall and can’t move till I let go of the moving buttons.
So, is there a way for there to be a flat hit. Meaning.. the circles stops on the edge of the rectangle instead of overlapping it a bit.
Thanks for any help!
Joe
2 Jan 12 at 6:07 pm edit_comment_link(__('Edit', 'sandbox'), ' ', ''); ?>