Author Topic: FlxCollision.pixelPerfectCheck not "pixel perfect" (Solved 100%!)  (Read 3041 times)

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
I made a triangle and a rectangle for my game and am sending them both to pixelPerfectCheck and the results are far from perfect. Are there any known ways to make this work at its best? I would post my code, but it is a simple boolean if statement. When pixelPerfectCheck returns true, it shows me. And it isn't even close to perfect.

When I run the normal flixel collisions function, I get the expected results.

EDIT: Here is more detail:

here is a screenshot of the collision test failing. I am falling my ship onto the platform. The reason it is stopped is because the game stops when the collision test returns true.



Here is the code that is being run on each update check (game is being run at 60 fps).  Note that "player" is the ship and "landingPad" is the white rectangle. Also keep in mind that the code outside of the collision check is sound. Using the normal collision check has given me perfect results outside of the fact that there is empty space between the two objects:

Code: [Select]

if(FlxCollision.pixelPerfectCheck(player, landingPad)){
player.hitLandingPad();
}



Here is the code of the pixelPerfectCheck that is being called:

Code: [Select]

public static function pixelPerfectCheck(contact:FlxSprite, target:FlxSprite, alphaTolerance:int = 255, camera:FlxCamera = null):Boolean
{
var pointA:Point = new Point;
var pointB:Point = new Point;

if (camera)
{
pointA.x = contact.x - int(camera.scroll.x * contact.scrollFactor.x) - contact.offset.x;
pointA.y = contact.y - int(camera.scroll.y * contact.scrollFactor.y) - contact.offset.y;

pointB.x = target.x - int(camera.scroll.x * target.scrollFactor.x) - target.offset.x;
pointB.y = target.y - int(camera.scroll.y * target.scrollFactor.y) - target.offset.y;
}
else
{
pointA.x = contact.x - int(FlxG.camera.scroll.x * contact.scrollFactor.x) - contact.offset.x;
pointA.y = contact.y - int(FlxG.camera.scroll.y * contact.scrollFactor.y) - contact.offset.y;

pointB.x = target.x - int(FlxG.camera.scroll.x * target.scrollFactor.x) - target.offset.x;
pointB.y = target.y - int(FlxG.camera.scroll.y * target.scrollFactor.y) - target.offset.y;
}

var boundsA:Rectangle = new Rectangle(pointA.x, pointA.y, contact.framePixels.width, contact.framePixels.height);
var boundsB:Rectangle = new Rectangle(pointB.x, pointB.y, target.framePixels.width, target.framePixels.height);

var intersect:Rectangle = boundsA.intersection(boundsB);

if (intersect.isEmpty() || intersect.width == 0 || intersect.height == 0)
{
return false;
}

// Normalise the values or it'll break the BitmapData creation below
intersect.x = Math.floor(intersect.x);
intersect.y = Math.floor(intersect.y);
intersect.width = Math.ceil(intersect.width);
intersect.height = Math.ceil(intersect.height);

if (intersect.isEmpty())
{
return false;
}

// Thanks to Chris Underwood for helping with the translate logic :)

var matrixA:Matrix = new Matrix;
matrixA.translate(-(intersect.x - boundsA.x), -(intersect.y - boundsA.y));

var matrixB:Matrix = new Matrix;
matrixB.translate(-(intersect.x - boundsB.x), -(intersect.y - boundsB.y));

var testA:BitmapData = contact.framePixels;
var testB:BitmapData = target.framePixels;
var overlapArea:BitmapData = new BitmapData(intersect.width, intersect.height, false);

overlapArea.draw(testA, matrixA, new ColorTransform(1, 1, 1, 1, 255, -255, -255, alphaTolerance), BlendMode.NORMAL);
overlapArea.draw(testB, matrixB, new ColorTransform(1, 1, 1, 1, 255, 255, 255, alphaTolerance), BlendMode.DIFFERENCE);

// Developers: If you'd like to see how this works, display it in your game somewhere. Or you can comment it out to save a tiny bit of performance
debug = overlapArea;

var overlap:Rectangle = overlapArea.getColorBoundsRect(0xffffffff, 0xff00ffff);
overlap.offset(intersect.x, intersect.y);

if (overlap.isEmpty())
{
return false;
}
else
{
return true;
}
}

« Last Edit: Sun, Feb 10, 2013 by Shadoninja »

wg/funstorm

  • Global Moderator
  • Key Contributor
  • *****
  • Posts: 596
  • Karma: +0/-0
    • View Profile
    • Funstorm
Pixelperfectcheck is pixel perfect. Post some code, and screenshots that show what isn't working for you so we can try to help you.

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
updated for you guys to better understand my struggle

wg/funstorm

  • Global Moderator
  • Key Contributor
  • *****
  • Posts: 596
  • Karma: +0/-0
    • View Profile
    • Funstorm
Looks like the sprite may have been rotated? If so, I believe that pixel perfect check only works if the sprite rotations have been precomputed, eg using FlxSprite's loadRotatedGraphic... but I could be wrong. In any case, notice in the FlxCollision code you posted

Code: [Select]
// Developers: If you'd like to see how this works, display it in your game somewhere. Or you can comment it out to save a tiny bit of performance
debug = overlapArea;

So try FlxG.stage.addChild(FlxCollision.debug) so that you can see what FlxCollision is seeing. If it's a rotation problem it should become obvious because then it should show the ship unrotated in the debug graphic. Make sure to check out the FlxCollision demos in the demos section of this page to give an example of what the debug image should look like http://www.photonstorm.com/flixel-power-tools

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
That was it! The bitmap data isn't updating. I have no idea how to update it efficiently though. If I load my sprite in using loadRotatedGraphic, it will update the bitmap info, but then rotating my ship looks choppy and terrible. Is there a better way?
« Last Edit: Sat, Feb 9, 2013 by Shadoninja »

wg/funstorm

  • Global Moderator
  • Key Contributor
  • *****
  • Posts: 596
  • Karma: +0/-0
    • View Profile
    • Funstorm
Tell loadrotated to generate a larger amount of rotations. Check out the demo I linked, the rotation appears smooth to me. If you look at his source code it will show you how he loaded it.

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
Yeah I actually grabbed his constructor for that. I even put the number of rotations (incrementally of course) all the way up to 2056 and it just never looks "smooth" like it did before. Is there any other way of getting the bitmap updated other than the loadRotatedGraphic method?

wg/funstorm

  • Global Moderator
  • Key Contributor
  • *****
  • Posts: 596
  • Karma: +0/-0
    • View Profile
    • Funstorm
Yea, you could modify the pixelperfectcheck function to apply the sprite's rotation to the bitmap.

Keep in mind using rotations without pre-generating them using loadrotated affects performance. It should look smooth with a large amount of rotations and antialiasing enabled.

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
I will mess with it more in the morning, but I assure you something wasn't lined up. Maybe there is another aspect of the function that was causing the bad visuals. It wasn't necessarily the frames looking choppy, but the way the game was handling the rotation and movement of the sprite. Rotating the sprite without being in motion looks fine. When I have the ship going somewhere AND rotate, then the thing just looks crappy. Its 1 AM here and I have work in the morning. I will take a look at it tomorrow evening with fresh eyes and make another post on my findings (if there are any).

photonstorm

  • Administrator
  • Key Contributor
  • *****
  • Posts: 1502
  • Karma: +1/-0
    • View Profile
    • Photon Storm
If loadRotatedSprite is visually too messy for you but good enough for collision then use a non-rendered sprite for the collision checks and place your 'real' one on-top of it so it still looks good.
http://www.photonstorm.com



"Tell me and I will forget, show me and I might remember, involve me and I will understand" - Confucius

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
That is genius! You guys are so smart. Thanks I will give that a try tonight when I get home.

Shadoninja

  • Member
  • **
  • Posts: 19
  • Karma: +0/-0
    • View Profile
Alright I am 99% there. Right now I am using a playerHitbox class that extends the original ship class, and I am calling all of their functions for movement at the same time in playState.  The final problem is that the rotation on playerHitbox isn't lining up with the rotation of the ship. I have set their origins to the same pixel location but haven't had luck fixing this yet.

EDIT: Figured it out! I was overlooking the sprites' transparent areas and just matched their canvas dimensions. Everything looks great now! Thanks for all the help
« Last Edit: Sun, Feb 10, 2013 by Shadoninja »