Code Snippet: Collision Detection

Demo

This bit of code uses blend modes to determine if two display objects intersect (if they are overlapping). It takes into account transparency, rotation, scale and translation. It enables near pixel level overlay detection. This forms the first part of any collision detection algorithm, firstly finding out if two objects intersect.

The idea was simple- draw two display objects onto a BitmapData and find the intersection with some fun use of blending modes. I was having difficulty with this initially and out of interest I took a look at how Richard did it in his Flixel Power Tools plugin set. Plus, his demo of this is way cooler than mine. To my luck, in FlxCollision he does exactly this.

Using FlxCollision pixelPerfectCheck as a base, the code was reworked to fit with the Adobe framework without the need for Flixel. I found out that there were a lot of helper methods provided by the SDK, which made the job of doing the collision detection easier. The most beneficial one, being able to get the transformation matrix that has been already applied to the display object. Another is the ability to get a bounding rectangle given a coordinate space.

Arguably the code is easier to read for some who do not know Flixel. A quick pseudo rundown of what is happening in the code below:

  1. determine the intersecting area from the bounding boxes
  2. create a bitmapdata object that is the size of the intersecting area
  3. draw the first object onto the bitmapdata using red and normal blend
  4. draw the second object onto the bitmapdata using white and difference blend
  5. all the while keeping their relative positions in tact
  6. get the rectangle which contains the light blue pixels (awesome method, getColorBoundsRect)
  7. if the rectangle is empty- there is no collision, otherwise there is

The code

package net.avdw.physics
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
	import flash.display.Stage;
	import flash.geom.ColorTransform;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;

	public function overlap(displayObjectA:DisplayObject, displayObjectB:DisplayObject, stage:Stage, overlapTolerance:int = 127, debugBmp:Bitmap = null):Boolean
	{
		if (debugBmp != null)
			debugBmp.bitmapData = null;

		var intersectionRect:Rectangle = displayObjectA.getRect(stage).intersection(displayObjectB.getRect(stage));
		if (intersectionRect.isEmpty())
			return false;

		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 (intersectionRect.isEmpty())
			return false;

		var overlapAreaData:BitmapData = new BitmapData(intersectionRect.width, intersectionRect.height);

		var matrixA:Matrix = displayObjectA.transform.matrix.clone();
		matrixA.translate(-intersectionRect.x, -intersectionRect.y);

		var matrixB:Matrix = displayObjectB.transform.matrix.clone();
		matrixB.translate(-intersectionRect.x, -intersectionRect.y);

		overlapAreaData.draw(displayObjectA, matrixA, new ColorTransform(0, 0, 0, 1, 255, -255, -255, overlapTolerance), BlendMode.NORMAL);
		overlapAreaData.draw(displayObjectB, matrixB, new ColorTransform(0, 0, 0, 1, 255, 255, 255, overlapTolerance), BlendMode.DIFFERENCE);

		if (debugBmp != null)
		{ 
			overlapAreaData.threshold(overlapAreaData, overlapAreaData.rect, new Point(), "!=", 0xFF00FFFF, 0);
			debugBmp.bitmapData = overlapAreaData;
			debugBmp.x = intersectionRect.x;
			debugBmp.y = intersectionRect.y;
		}

		var overlapRect:Rectangle = overlapAreaData.getColorBoundsRect(0xFFFFFFFF, 0xFF00FFFF);

		if (overlapRect.isEmpty())
			return false;

		return true;
	}

}