Pixel Perfect Collision Detection


#1

I have a US States game that I am converting from Flash (AS3), however each state is its own separate image (.png) and rests within a bounding box. Does Haxe + OpenFL have any way of ONLY detecting when users click on the state itself and not the entire box?


#2

Yep, I believe our hit testing should be based on the visible portion of an image, not the full image


#3

I guess I never really tested that to be sure. Great improvement from AS3!


#4

Just make sure you put “true” on the second parameter, right?


Edit: Wait… what happens with transparent pixels?


Edit again: Just double checked real fast, on HTML5-Canvas HitTestPoint doesn’t know how to ignore transparent pixels.

This is the code I use to do pixel perfect point collision

	public static function pixelPerfectHitTest(object:DisplayObject, point:Point):Bool {
		/* If we're already dealing with a BitmapData object then we just use the hitTest
		 * method of that BitmapData.
		 */
		if(Std.is(object,Bitmap)) {
			return cast(object,Bitmap).bitmapData.hitTest(new Point(0,0), 0, object.globalToLocal(point));
		}
		else
		{
				
			/* First we check if the hitTestPoint method returns false. If it does, that
			 * means that we definitely do not have a hit, so we return false. But if this
			 * returns true, we still don't know 100% that we have a hit because it might
			 * be a transparent part of the image. 
			 */
			if(!object.hitTestPoint(point.x, point.y, true)) {
				return false;
			}
			else {
				/* So now we make a new BitmapData object and draw the pixels of our object
				 * in there. Then we use the hitTest method of that BitmapData object to
				 * really find out of we have a hit or not.
				 */
				var bmapData:BitmapData = new BitmapData(Std.int(object.width), Std.int(object.height), true, 0x00000000);
				bmapData.draw(object, new Matrix());
				
				var returnVal:Bool = bmapData.hitTest(new Point(0,0), 0, object.globalToLocal(point));
				
				bmapData.dispose();
				
				return returnVal;
			}
		}
	}

#5

Great. I’ll give that a try, becuase this:

if (state[currentState].hitTestPoint(mouseX, mouseY, true)) { //do stuff }

Wasn’t working.


#6

Keep in mind that that function is designed to be generic but not efficient. If you have a button that you expect to remain the same size you should cache the bitmapdata instead of .draw()ing it all the time.

.draw() is a big bad monster that will eat all your performance

(at least on Software renderers).


#7

sounds like every state is a separate bitmap, which means you don’t have to draw them to another bitmap to do the detection.

if (bitmap.hitTestPoint(bitmap.mouseX, bitmap.mouseY)
    && (bitmap.bitmapData.getPixel32(Math.round(bitmap.mouseX), Math.round(bitmap.mouseY)) >>> 24) > 0x00)
{
    trace("Hit");
}

you might have to account for the bitmap’s scaling if they are not scaled 1.0. or use globalToLocal depending on what your source point is. but this essentially finds whether the point is touching the bitmap, then finds the alpha of the pixel under that point (color >>> 24 gets the alpha channel)


#8

If you can get to the bitmapdata, why no use hitTest? Is this better?


#9

I don’t follow… I am using hitTestPoint in my example just to check that the point overlapps the entire bitmap (alpha or not) than I check the pixel at that point, since hitTestPoint with shapeflag doesn’t exclude 0 alpha parts of a bitmap


#10

Bitmapdata has a special function called “hitTest” that only checks non alpha pixels. Is not the same as bitmap’s hittestpoint


#11

Oh nice! I was unaware of this method. I assumed it behaved just like every other display object’s hitTest method. yeah that would totally work


#12

It does, this is great, but it asks for two arguments (2 points). What two points would i use? (I’m only wanting to see if the mouse point click on the pixels of the BitmapData.)


#13

Why 2 points? Or it is only one point (x,y) or the 2 coordinates separately X and Y


#14

I only want to test the BitmapData to the mouse’s click, but but the “.hitTest” is asking for two arguments.


#15

Whenever the OpenFL api seems criptic, close your eyes and plunge into the old AS3 reference and hope it still applies.

https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html#hitTest()

Parameters

firstPoint:Point — A position of the upper-left corner of the BitmapData image in an arbitrary coordinate space. The same coordinate space is used in defining the secondBitmapPoint parameter.

firstAlphaThreshold:uint — The smallest alpha channel value that is considered opaque for this hit test.

secondObject:Object — A Rectangle, Point, Bitmap, or BitmapData object.

secondBitmapDataPoint:Point (default = null) — A point that defines a pixel location in the second BitmapData object. Use this parameter only when the value of secondObject is a BitmapData object.

secondAlphaThreshold:uint (default = 1) — The smallest alpha channel value that is considered opaque in the second BitmapData object. Use this parameter only when the value of secondObject is a BitmapData object and both BitmapData objects are transparent.


#16

…and when it doesn’t, let me know if there’s no good reason for the difference :slight_smile: