I've finally gotten around to updating my pixel-perfect collision detection post (which is by far the most popular post on this blog with 48+ comments and a double-digit percentage of all my pageviews). Of course, now that I've gotten around to updating it there's not much point, as others have improved upon and trumped what I had. So, I'll just provide a quick recap of their pursuits (and my version of them).
First, The Actionscript Man grabbed my version, Grant Skinner's (which mine was indirectly based on), digested them, then spit out a new one that eliminated some draw calls. He even made a nice little app that lets you toggle between the various versions and compare them. The only drawback of his version is that it assumes the two objects have the same parent.
Next, the venerable Tink posted his version that uses the same technique as The Actionscript Man but factors a more complicated parent-child relationship. Tink's still assumes that both objects are on the stage. His code also gets bitten by a bug I've run into related to localToGlobal and nested SWFs (though I still haven't explored it enough to write about it yet), as discussed in the comments. Tink added an "accuracy" parameter that basically scales the bitmap used for testing up and down. I've not found this makes enough of a performance difference to warrant the dramatic differences it can make in a pixel-perfect test.
So, I updated my version to include the rendering method Tink and The Actionscript Man used, and included the "common parent" math my original one had. The upside of my version is that it doesn't care whether your objects are on the stage or not, just that they share a common parent.
-
/** Get the collision rectangle between two display objects. **/
-
public static function getCollisionRect(target1:DisplayObject, target2:DisplayObject, commonParent:DisplayObjectContainer, pixelPrecise:Boolean = false, tolerance:int = 255):Rectangle
-
{
-
// get bounding boxes in common parent's coordinate space
-
var rect1:Rectangle = target1.getBounds(commonParent);
-
var rect2:Rectangle = target2.getBounds(commonParent);
-
-
// find the intersection of the two bounding boxes
-
var intersectionRect:Rectangle = rect1.intersection(rect2);
-
-
// if not pixel-precise, we're done
-
if (!pixelPrecise) return intersectionRect;
-
-
// size of rect needs to be integer size for bitmap data
-
intersectionRect.x = Math.floor(intersectionRect.x);
-
intersectionRect.y = Math.floor(intersectionRect.y);
-
intersectionRect.width = Math.ceil(intersectionRect.width);
-
intersectionRect.height = Math.ceil(intersectionRect.height);
-
-
// if the rect is empty, we're done
-
if (intersectionRect.isEmpty()) return intersectionRect;
-
-
// calculate the transform for the display object relative to the common parent
-
var parentXformInvert:Matrix = commonParent.transform.concatenatedMatrix.clone();
-
parentXformInvert.invert();
-
var target1Xform:Matrix = target1.transform.concatenatedMatrix.clone();
-
target1Xform.concat(parentXformInvert);
-
var target2Xform:Matrix = target2.transform.concatenatedMatrix.clone();
-
target2Xform.concat(parentXformInvert);
-
-
// translate the target into the rect's space
-
target1Xform.translate(-intersectionRect.x, -intersectionRect.y);
-
target2Xform.translate(-intersectionRect.x, -intersectionRect.y);
-
-
// combine the display objects
-
var bd:BitmapData = new BitmapData(intersectionRect.width, intersectionRect.height, false);
-
bd.draw(target1, target1Xform, new ColorTransform(1, 1, 1, 1, 255, -255, -255, tolerance), BlendMode.NORMAL);
-
bd.draw(target2, target2Xform, new ColorTransform(1, 1, 1, 1, 255, 255, 255, tolerance), BlendMode.DIFFERENCE);
-
-
// find overlap
-
var overlapRect:Rectangle = bd.getColorBoundsRect(0xffffffff, 0xff00ffff);
-
overlapRect.offset(intersectionRect.x, intersectionRect.y);
-
-
return overlapRect;
-
}
Finally, if you really want some sophisticated, fully-featured, pixel-perfect collision detection *and* handling, check out Corey O'Neil's Collision Detection Kit. I haven't used it yet, but it looks pretty comprehensive with good performance.
This looks pretty expensive! I wonder if some caching could be used to avoid lots of the expensive work in here. I’ll bet a lot of this would be made possible if you knew what kind of assets you were using. For example, if your assets never resize, you could probably keep using the same BitmapData over and over. Just a thought. Thanks for the article!
I’m trying to make a simple car game where the player uses the keys to make the (rectangle) car go around the track. When it hits the wall (for now) I just want it to back up at a certain speed so that the user can correct it and continue around the track. I’m using your class, calling it in a function that’s fun on an ENTER_FRAME and it works fine, to an extent.
I’m having problems though, because, obviously, in a car game the car doesn’t always face the same direction: it gets rotated. What happens to me is that when the rotated car hits a wall, sometimes it gets stuck in the wall and doesn’t bounce back. Any ideas as to what I need to do differently so it bounces back no matter the orientation?
This collision detection code is not best-suited for this scenario. For what you’re describing, I’d recommend checking out one of the rigid body physics solutions. Box2D is well-maintained, well-documented, and has a healthy featureset. APE, which would handle the scenario you’re describing, is very, very easy to use (though doesn’t appear to be maintained any more).
I can't figure out how to use this class :( I figured out Skinner's class perfectly, since it's AS file followed a structure I'm used to work with. This, I can't. How do I pass the movieclips to be tested and how do I return them?
Thanks for your work.
Well I think I got it. But nontheless, this solution still doesn't solve the rotation problem. My transparent objects, when rotated and still separated by as much as 20px (joined by transparent pixels of course) performs true on the hit test. Am I doing something else wrong?
Thanks!
Hi, i'm just using your detection script.
First of all, i'd like to say it is great, works very smooth.
Second, considering the topic of this blog you shouldn't endorse nor advice using CDK as i come directly from it, and i call tell you is very heavy, the drop perfomance is noticeable.
CDK is good, and has plenty of funcitons, but for gaming is not suitable.
Question:
I was looking at the code of this detection script, but as i dont really underestand it so much, i cannot judge how perfomance-friendly it be.
So i dare to question you some things:
-do you thing it has a good preformance, do you think could exist some others ways to achieve perfect collition detections with a huge amount of perfomance difference?
-Trying to get collition detections with pixelPrecise=false attribute is better than hitTestObject?
Great, thax
This runs great so long as I don't set the “pixelPrecise” argument to “true”. Once I do that then the collision rectangle never get made, its width and height remain at 0. I think I'm implementing this correctly and I've toyed with the tolerance and with various colliding objects, all to no effect. When the boolean is set to “false” this runs like a dream. Much faster than the CDK, which I've also used and was disappointed with the ridiculous lag.
This runs great so long as I don't set the “pixelPrecise” argument to “true”. Once I do that then the collision rectangle never get made, its width and height remain at 0. I think I'm implementing this correctly and I've toyed with the tolerance and with various colliding objects, all to no effect. When the boolean is set to “false” this runs like a dream. Much faster than the CDK, which I've also used and was disappointed with the ridiculous lag.
YOU RULE
THANKS :D