For a flash game project, i needed to see if a number of sprites had collided. I soon found out that hitTestObject doesn't support perfect collisions for non-shape sprites, so i needed another solution.

     After wasting some time discovering hitTestObject only supports bounding rectangles for non shape sprites, i needed to find a pixel perfect collision detection method that worked on any sprites. One of the ways i've done it in the past is quite common, and has been used a lot over the years. You can approximate most sprites fairly well using a series of circles to form a collision detection area. Then it's fairly easy and reasonably fast to see if any circles have collided. This time however, i wanted to use pixel perfect accuracy, but i wasn't sure if flash was powerful enough to cope with such a task. It turns out that Flash Player 9 and actionscript 3 handle the job reasonably well with a clever bit of code from some smart cookies.

     So the first thing todo was to google and find out if this had been done before, it's not always worth reinventing the wheel, if someone has already found a good way of doing it. I found a few versions all based off the same code by a chap called Grant Skinner. The version he generously gave away is for flash 8 and actionscript 2. I also found a slightly different variation for actionscript 3 by Troy Gilbert. I wanted to know which worked best overall (they both have slightly different features) and also if i could create anything better, so i knocked up a quick test to test the functions. And produced a little test app.

Hit Test Pixel Perfect Screen Shot

     You can click to open the test swf here. Be aware that during testing using the skinner function, it crashed a few times and i'm not sure why. My method seemed to be ok but that could just have been luck! Don't blame me if lots of large scaled objects crash your web browser! You can use the sliders to adjust the number of objects, and the scale of the objects. The objects overlap and stick together sometimes due to the bounce code used - it's just a quick test, the bounce code is from that nice Emanuele Feronato. I could fix it by moving the objects enough away from eachother, but it doesn't matter for a quick test in my opinion and it makes some neat patterns when they stick together :) You can select between sprite & shape (although they are both drawn into a sprite) and the four available methods for collision detection, adobe (hitTestObject), skinner, gilbert, and my variation which is based on both of the previous with a couple of tweaks. No one method will be optimal in all cases, so i'll provide my method along with the script i used to pick between the different routines.

     Pixel perfect collision detection is a complex and non-trival problem, and there are many ways to solve it. I have another few ideas to try when i get the chance, Grant Skinner has some proximity test code using grids to track objects which would speed up the sprite to sprite collision detection by pruning the number of other sprites you need to check against, just down to the local area. For lots of objects this would definitely be a worthwhile speed up, and i may try that if i need to in the future, but for now i'm only checking 8 or so sprites together. The function below is about as fast as i can get it under the current test method, and it's plenty fast enough for what i need.

     As promised heres the code to my collision detection function, it returns an intersecting rectangle, so to simply test for collisions you could call it with (hitTestObject(object1,object2) != null).

Actionscript Code Highlighted with GeSHi © 2004, Nigel McNie

/*
 * Adventures In Actionscript AS3 Toolkit
 * Copyright (C) 2008 www.adventuresinactionscript.com
 *
 * If you use this code in your own projects, please give credit to
 * the authors and feel free to let them know about your projects that
 * make use of this. You are not authorized to distribute modified
 * copies of this code, without first contacting all the authors and
 * obtaining their permission. You may however modified and use this
 * code in your own compiled projects without permission. Do not remove
 * or modify this header in anyway.
 *
 * The contents of this file are subject to the Mozilla Public License
 * Version 1.1 (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
 * License for the specific language governing rights and limitations
 * under the License.
 */

 
package com.adventuresinactionscript.collisions {

     /**
     * A replacement collision detection class
     * @author The Actionscript Man (theactionscriptman [at] gmail.com)
     * @version 1.0.0
     */

     public class HitTest {
          import flash.display.DisplayObject;
          import flash.display.BitmapData;
          import flash.geom.ColorTransform;
          import flash.geom.Matrix;
          import flash.geom.Rectangle;
          
          /**
           * hitTestObject checks to see if two display objects have collided from
           * http://www.adventuresinactionscript.com/blog/15032008/actionscript-3-hit...
           * Based on Grant Skinner & Troy Gilberts collision detection functions
           *
           * @param  object1      the first object to be tested
           * @param  object2      the second object to be tested
           * @param  pixelPerfect check bounding rectangle only if set to false
           * @param  tolerance    alpha tolerance value
           * @return intersecting rectangle if the objects have collided, or null if no collision
           * @see         hitTestObject
           */
 
          static public function hitTestObject(object1:DisplayObject, object2:DisplayObject, pixelPerfect:Boolean=true, tolerance:int = 255):Rectangle {
               
               // quickly rule out anything that isn't in our hitregion
               if (object1.hitTestObject(object2)) {
               
                    // get bounds:
                    var bounds1:Rectangle = object1.getBounds(object1.parent);
                    var bounds2:Rectangle = object2.getBounds(object2.parent);
                                   
                    // determine test area boundaries:
                    var bounds:Rectangle = bounds1.intersection(bounds2);
                    bounds.x = Math.floor(bounds.x);
                    bounds.y = Math.floor(bounds.y);
                    bounds.width = Math.ceil(bounds.width);
                    bounds.height = Math.ceil(bounds.height);
                              
                    //ignore collisions smaller than 1 pixel
                    if ((bounds.width < 1) || (bounds.height < 1))
                         return null;
                         
                    if (!pixelPerfect)
                         return bounds;
               
                    // set up the image to use:
                    var img:BitmapData = new BitmapData(bounds.width, bounds.height, false);
                    
                    // draw in the first image:
                    var mat:Matrix = object1.transform.concatenatedMatrix;
                    mat.translate( -bounds.left, -bounds.top);
                    img.draw(object1,mat, new ColorTransform(1,1,1,1,255,-255,-255,tolerance));
                    
                    // overlay the second image:
                    mat = object2.transform.concatenatedMatrix;
                    mat.translate( -bounds.left, -bounds.top);
                    img.draw(object2,mat, new ColorTransform(1,1,1,1,255,255,255,tolerance),"difference");
                    
                    // find the intersection:
                    var intersection:Rectangle = img.getColorBoundsRect(0xFFFFFFFF,0xFF00FFFF);
                    
                    // if there is no intersection, return null:
                    if (intersection.width == 0) { return null; }
                    
                    // adjust the intersection to account for the bounds:
                    intersection.offset(bounds.left, bounds.top);
                    
                    return intersection;
               }
               else
                    return null;
          }         
          
     }
}


Heres a link to the above function in an as file HitTest.as and the class i used in the test bouncing object app TestHitTest.as. Remeber that if you can, you should always use the built in hitTestObject function for speed, but also remember that it only detects rectangles. If you can live with that, then it's a very fast function, if you can't then give my function a try!

I hope someone else finds this useful, if you have any comments or suggestions let me know. This was my first actual blog on here about code :)