Author Topic: Pixel perfect overlap for Flixel !!! NOW EVEN BETTER !!  (Read 16510 times)

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
Hi people..
I bothered with this for a while so since i have it working now i decided to share it with this great community since i know this is really needed in lot of projects..
So i give u pixel perfect overlap function ! With it you can test are 2 objects overlapping each other..
Based on Tink's algorithm : http://www.tink.ws/blog/as-30-hittest/ .
Thx Mirosabu for assistance!

FEATURES :
- works for any shape
- works for any offset or origin
- works perfectly even when objects are rotated
- works for any scale
- works for animated sprites
- works for scrolling


<a href="http://mirosurabu.com/flxplus/PixelPerfectOverlap.swf" target="_blank" rel="noopener noreferrer" class="bbc_link bbc_flash_disabled new_win">http://mirosurabu.com/flxplus/PixelPerfectOverlap.swf</a>

Download example source
Download FlxHitTest.as (view pastebin)

Got rid of dummies and its now only one class.
Should work with latest version of Flixel, with any origin, offset, angle and scale.

Use complexHitTestObject(target1:FlxSprite, target2:FlxSprite, accuracy:Number) to test for overlap. Make sure you specify at least 6 for accuracy if you want precise intersection detection.

NOTE: You are require to make the _framePixels member of FlxSprite public
« Last Edit: Mon, Jun 28, 2010 by xraven13 »

TheSidehatch

  • Guest
Re: Pixel perfect overlap for Flixel !!!
« Reply #1 on: Sun, Apr 11, 2010 »
I created an account just to thank you - this worked perfectly for me after a few small changes.  Scrolling seemed to screw things up so in the FlxSpriteEx's update function I put dummy.x = x + scrollx, and the same for y. 

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
Re: Pixel perfect overlap for Flixel !!!
« Reply #2 on: Thu, Apr 15, 2010 »
Great, i am glad someone like it :).. And thanks for mentioning scrolling!

zuperxtreme

  • Contributor
  • ****
  • Posts: 254
  • Karma: +0/-0
    • View Profile
    • Buddah
Re: Pixel perfect overlap for Flixel !!!
« Reply #3 on: Thu, Apr 15, 2010 »
Looks great! I might just use this.
..."without order nothing exists, without chaos nothing evolves"... 
Zoklet.net

kishi

  • Guest
Re: Pixel perfect overlap for Flixel !!!
« Reply #4 on: Sat, Apr 24, 2010 »
exactly what i needed thx

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
Re: Pixel perfect overlap for Flixel !!!
« Reply #5 on: Sun, Apr 25, 2010 »
BTW i am not sure does this works if offset of Sprite and FlxSprite is different then width/2, height/2..
I am sure they shouldn't be different..
I will be soon updating the code with ability to work for animations that change shapes and will see what i can do with offset thingy :p

mirosurabu

  • Member
  • **
  • Posts: 34
  • Karma: +0/-0
    • View Profile
Re: Pixel perfect overlap for Flixel !!!
« Reply #6 on: Sun, Apr 25, 2010 »
Mirosurabu not Mirosabu! :P

By the way, the reason this doesn't work when scaling is applied is because we didn't set our FlxSpriteEx to update the scale properties of the dummy sprite.

So adding this line of code in update() function of FlxSpriteEx...

Code: [Select]
dummy.scaleX = scale.x;
dummy.scaleY = scaly.y;

...would hopefully make overlap work with scaled sprites.

On a side not, this all could be solved without using native flash sprites. But I have trouble using hitTest with BitmapData's. Another version of this code would be the one that creates dummy sprites when you call the complexHitTestObject() though that might hit the performance significantly.
« Last Edit: Sun, Apr 25, 2010 by mirosurabu »

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
Hey everyone !!
I bring good news... :D
Pixel perfect overlap got improved a lot and now :
- works for any origin
- works for any scale
- works for scrolling
- works for animated sprites

I explained all in first post so check it out. There is new code and explanation how all works.
Thx to Mirosurabu ! I am more like a postman ^^...

Garmichael

  • Member
  • **
  • Posts: 89
  • Karma: +0/-0
    • View Profile
I have a pseudo-3d perspective in my game, sort of like in Zelda 3. The player controls a ship that fires visible bullets.

One enemy type I have is a gun turret, shaped like a cylinder. When firing at it, I want bullets that hit its base area to make contact, but the bullets should also go behind the turret if it doesnt hit the base.

So, could I (or should I) use this class to detect a sprite with an alpha of 0, hidden and overlayed on top of the turret, meant to be the acceptable hit area?

mirosurabu

  • Member
  • **
  • Posts: 34
  • Karma: +0/-0
    • View Profile
Garmichael,

Yes I think you can. When you create an instance of this new FlxSpriteEx class, a dummy mask of a display object type (SpriteEx) is created and added on stage, but it's invisible. This mask contains the bitmap of the Flixel sprite and follows the main Flixel sprite in that it snaps to its position, angle and scale. But no alpha value is changed to the mask. So even if you make the Flixel sprite alpha-transparent and invisible the background display object mask will still be detected for collision/intersection.

FlxSpriteEx builds on top of Flixel's FlxSprite and its very easy to use. You use constructor or loadGraphic() to load the image but you have to use setOrigin() to adjust the origin and offset. That's all.

The ugly thing about this class is that it makes use of native flash sprites because the HitTest code works only on display objects and I have no patience to make work with BitmapData.

mirosurabu

  • Member
  • **
  • Posts: 34
  • Karma: +0/-0
    • View Profile
<a href="http://mirosurabu.com/flxplus/PixelPerfectOverlap.swf" target="_blank" rel="noopener noreferrer" class="bbc_link bbc_flash_disabled new_win">http://mirosurabu.com/flxplus/PixelPerfectOverlap.swf</a>

Download example source
Download FlxHitTest.as (view pastebin)

Got rid of dummies and its now only one class.
Should work with latest version of Flixel, with any origin, offset, angle and scale.

Use complexHitTestObject(target1:FlxSprite, target2:FlxSprite, accuracy:Number) to test for overlap. Make sure you specify at least 6 for accuracy if you want precise intersection detection.

NOTE: You are require to make the _framePixels member of FlxSprite public
« Last Edit: Mon, May 3, 2010 by mirosurabu »

cai

  • Contributor
  • ****
  • Posts: 465
  • Karma: +0/-0
  • the illest of villains
    • View Profile
    • Brandon Cash
The ugly thing about this class is that it makes use of native flash sprites because the HitTest code works only on display objects and I have no patience to make work with BitmapData.
BitmapData has its own built-in hitTest(), too.

Take for instance this line from my latest project, where ground is a BitmapData object:
Code: [Select]
ground.hitTest(new Point(0, 0), 255, new Point(x, y + 1))
Follow me on Twitter | Come join us at #flixel on irc.freenode.net!

mirosurabu

  • Member
  • **
  • Posts: 34
  • Karma: +0/-0
    • View Profile
Yes, that's true.

What I actually meant is that Tink's code works with display object's transformations so rewriting his code was kind of scarier than using dummy sprites.

Turns out rewriting Tink's code to work with FlxSprite's was easier than expected.

romamik

  • Guest
Re: Pixel perfect overlap for Flixel !!! NOW EVEN BETTER !!
« Reply #13 on: Fri, May 21, 2010 »
I've create two classes for pixel perfect collision test.
One extends FlxSprite and adds overlapsPP function to it.
Another contains overlapPP function similar to FlxU.overlap, but for pixel perfect testing. It does not require to modify flixel.

Also it is very easy to integrate into flixel. Just add these methods to FlxSprite and FlxU classes and thats all.

Demo: http://www.swfcabin.com/open/1276274718
Demo project for FlashDevelop: http://www.mediafire.com/file/mb0l2ucd0n4/flixelPP.zip

Note that overlapPP may be inaccurate if collision is small (1 pixel or may be 1x2 pixels), this is due to bug in BitmapData.getColorBoundsRect: sometimes it returns zero rectangle even if one pixel is of desired color.
I believe that for big sprites this is not the problem. For small sprites vs big sprites I recommend to use overplapsPointPP function. For small sprites it's enough to check if they are close enough to each other I think :-)

If you use overlapsPP or overplapsPointPP don't forget to call preparePP for each object. (This is not needed for FlxUPP.overlapPP)

Also note that FlxUPP.overlapPP do not use quad tree so be careful when using against big groups of objects. I'm intersted if it is possible to use flixel's quad tree for this function. AFAIK it's not aware of rotation, scaling, scroll factors and so on.

EDIT (26 may 2010): fixed bug in FlxUPP.overlapPP
EDIT (11 june 2010): add overlapsPointPP function.
« Last Edit: Fri, Jun 11, 2010 by romamik »

Arno

  • Guest
Re: Pixel perfect overlap for Flixel !!! NOW EVEN BETTER !!
« Reply #14 on: Fri, May 21, 2010 »
This is absolutely amazing. It's still a little beyond me as I'm still learning to program, but I could really use something like this in one of my games as I have animated objects that only become obstacles on a certain frame in their animation. This would make it pixel perfect indeed. GOOD WORK! *not just two, but three thumbs up*

RandomNinja

  • Guest

EDIT (26 may 2010): fixed bug in FlxUPP.overlapPP


Hey romamik, very nice! Does it work if the sprite is rotated?

I found a small bug: In your overlapPP function, you should check whether the sprites are solid. I fixed like so

Code: [Select]
....
else if (obj1 is FlxSprite)
{
...
else if(obj2 is FlxSprite)
{
if (!obj1.solid || !obj2.solid)
return false;

Or else unsolid objects will collide :)

Entire function:
Code: [Select]
public static function overlapPP(obj1:FlxObject, obj2:FlxObject, callback:Function = null):Boolean
{
if (!obj1 || ! obj1.exists || !obj2 || !obj2.exists)
return false;

var retVal:Boolean = false;

if (obj1 is FlxGroup)
{
for each(var subObj1:FlxObject in (obj1 as FlxGroup).members)
retVal = overlapPP(subObj1, obj2, callback) || retVal;
return retVal;
}
else if (obj1 is FlxSprite)
{
if (obj2 is FlxGroup)
{
for each(var subObj2:FlxObject in (obj2 as FlxGroup).members)
retVal = overlapPP(obj1, subObj2, callback) || retVal;
return retVal;
}
else if(obj2 is FlxSprite)
{
if (!obj1.solid || !obj2.solid)
return false;
var rect:Rectangle = (obj1 as FlxSprite).overlapsPP(obj2 as FlxSprite)
if (!rect)
return false;

if (callback != null)
{
var p:FlxPoint = new FlxPoint(rect.x + rect.width / 2, rect.y + rect.height / 2)
callback(obj1, obj2, p);
}
else
{
obj1.kill();
obj2.kill();
}

return true;
}
}

return false;
}

romamik

  • Guest
Yes, it works if the sprite is rotated, scaled or anything else.

And I just don't know what solid means in flixel...

RandomNinja

  • Guest
As far as I've understood, 'solid' just says whether you want the object to be affected by collision detection...

If you've killed something for instance, and want to play a 'die'-animation, but don't want that animation to interfere with collisions.

might also work as a sort of "noclip" mode.
« Last Edit: Sun, Jun 6, 2010 by RandomNinja »

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
BTW does this works for anyone with zoom bigger then 1 ??
This worked perfectly in all my projects until this one where zoom is equal to 4...
Not sure why, but its not working, all sprites are scaled 4 times, but i think there might be messed something with coordinate system of state... Any idea ?

xraven13

  • Active Member
  • ***
  • Posts: 213
  • Karma: +0/-0
    • View Profile
    • GemaNeko
I've create two classes for pixel perfect collision test.
One extends FlxSprite and adds overlapsPP function to it.
Another contains overlapPP function similar to FlxU.overlap, but for pixel perfect testing. It does not require to modify flixel.

Also it is very easy to integrate into flixel. Just add these methods to FlxSprite and FlxU classes and thats all.

Demo: http://www.swfcabin.com/open/1274435930
Demo project for FlashDevelop: http://www.mediafire.com/file/jtnmzemzyly/flixelPP.zip
Code:
Code: [Select]
/*
 * This code is in public domain.
 */
package org.flixelPP
{
import flash.display.*;
import flash.geom.*;
import org.flixel.*;

public class FlxSpritePP extends FlxSprite
{
public function FlxSpritePP(X:Number=0,Y:Number=0,SimpleGraphic:Class=null)
{
super(X, Y, SimpleGraphic);
}

/**
* Checks to see if some <code>FlxSpritePP</code> object overlaps this <code>FlxSpritePP</code> object.
* Works on pixel perfect level.
*
* @param obj The object being tested.
*
* @return intersection rectangle if any, or null.
*/
public function overlapsPP(obj:FlxSpritePP):Rectangle
{
preparePP();
obj.preparePP();

var bounds:Rectangle = _rectPP.intersection(obj._rectPP);
bounds.left = Math.floor(bounds.left);
bounds.top = Math.floor(bounds.top);
bounds.width = Math.ceil(bounds.width);
bounds.height = Math.ceil(bounds.height);

if (bounds.width < 1 || bounds.height < 1)
return null;

var bmp:BitmapData = new BitmapData(bounds.width, bounds.height, false);

_mtxPP.translate( -bounds.x, -bounds.y);
// draw red
bmp.draw(_framePixels, _mtxPP, new ColorTransform(1, 1, 1, 1, 255, -255, -255, 255));

// draw green
if(obj !== this)
obj._mtxPP.translate( -bounds.x, -bounds.y);
bmp.draw(obj._framePixels, obj._mtxPP, new ColorTransform(1, 1, 1, 1, -255, 255, -255, 255), BlendMode.ADD);

// search for red+green
var intersection:Rectangle = bmp.getColorBoundsRect(0xFFFFFFFF, 0xFFFFFF00);

if (intersection.width < 1 || intersection.height < 1)
return null;

intersection.offset(bounds.x, bounds.y);

return intersection;
}

//private
private function preparePP():void
{
// _mtxPP - transformation matrix (same as in render())
getScreenXY(_point);
_mtxPP.identity();
_mtxPP.translate(-origin.x,-origin.y);
_mtxPP.scale(scale.x,scale.y);
if(angle != 0) _mtxPP.rotate(Math.PI * 2 * (angle / 360));
_mtxPP.translate(_point.x + origin.x, _point.y + origin.y);

//_rectPP - bounding box
var p1:Point = new Point(0, 0);
var p2:Point = new Point(width, 0);
var p3:Point = new Point(0, height);
var p4:Point = new Point(width, height);
p1 = _mtxPP.transformPoint(p1);
p2 = _mtxPP.transformPoint(p2);
p3 = _mtxPP.transformPoint(p3);
p4 = _mtxPP.transformPoint(p4);
_rectPP.left = Math.min(p1.x, p2.x, p3.x, p4.x);
_rectPP.right = Math.max(p1.x, p2.x, p3.x, p4.x);
_rectPP.top = Math.min(p1.y, p2.y, p3.y, p4.y);
_rectPP.bottom = Math.max(p1.y, p2.y, p3.y, p4.y);
}

private var _rectPP:Rectangle = new Rectangle;
private var _mtxPP:Matrix = new Matrix;
}

}

Code: [Select]
/*
 * This code is in public domain.
 */
package org.flixelPP
{
import flash.geom.*;
import org.flixel.*;

public class FlxUPP
{
/**
* Checks pixel perfect overlaps between FlxSpritePP's and FlxGroup's.
* Note that this function ignores anything except FlxGroup and FlxSpritePP.
*
* @param Object1 The first FlxSpritePP or group you want to check.
* @param Object2 The second FlxSpritePP or group you want to check.  Can be the same as the first.
* @param Callback e.g. <code>myOverlapFunction(Object1:FlxObject, Object2:FlxObject, Point:FlxPoint);  If no function is provided, overlapPP will call <code>kill()</code> on both objects.
*/
public static function overlapPP(obj1:FlxObject, obj2:FlxObject, callback:Function = null):Boolean
{
if (!obj1 || ! obj1.exists || !obj2 || !obj2.exists)
return false;

var retVal:Boolean = false;

if (obj1 is FlxGroup)
{
for each(var subObj1:FlxObject in (obj1 as FlxGroup).members)
retVal = overlapPP(subObj1, obj2, callback) || retVal;
return retVal;
}
else if (obj1 is FlxSpritePP)
{
if (obj2 is FlxGroup)
{
for each(var subObj2:FlxObject in (obj2 as FlxGroup).members)
retVal = overlapPP(obj1, subObj2, callback) || retVal;
return retVal;
}
else if(obj2 is FlxSpritePP)
{
var rect:Rectangle = (obj1 as FlxSpritePP).overlapsPP(obj2 as FlxSpritePP)
if (!rect)
return false;

if (callback != null)
{
var p:FlxPoint = new FlxPoint(rect.x + rect.width / 2, rect.y + rect.height / 2)
callback(obj1, obj2, p);
}
else
{
obj1.kill();
obj2.kill();
}

return true;
}
}

return false;
}
}
}

EDIT (26 may 2010): fixed bug in FlxUPP.overlapPP



Dude, kudos for u =D..
Didn't test it 100% , but this worked perfectly with zoom while other versions don't....
Thanks a lot !!! =)

EDIT: ok after more testing it seems it doesn't work with pictures that are really small =(.. I tested and it doesn't work if width or height is 1 pixel... For 2 pixels it works ... But i need to work for 1 pixels XD
« Last Edit: Wed, Jun 9, 2010 by xraven13 »