4
\$\begingroup\$

I've got a roughly 500x50 graphic of a lockpick that I need to be able to rotate very finely while doing pixel-perfect collision detection against the lock object.

There's no way to do pixel perfect collision detection in Flixel, so I'm using the Power Tools' FlxCollision class to accomplish this. The problem is that this class only works with rotated sprites if they were loaded with loadRotatedGraphic.

...the problem with that is that it only allows you to rotate to discrete angles. You pass it how many angles you need to be able to rotate to and it, I think, pre-prepares all the frames for you.

And the reason that's a problem is because I need such fine rotation. I need at least 128 different angle values, but when I do that I get Error 2015: Invalid Bitmap, which I'm assuming is because the resulting 128 frames of a 500x50 lockpick results in a bitmap larger than AS3's limit. I can load the graphic using loadGraphic instead, which I think just calculates the rotated image real-time, but then FlxCollision won't notice that it's been rotated. Another problem is that with the pre-rotated graphics, you can't rotate about a specific point, and I need the lockpick to rotate around the tip of its handle.

...so how the heck do I do pixel perfect collision against a rotated sprite? I'm trying to mod FlxSprite so that FlxCollision will see the rotated sprites even when they're calculated in real time, and to do this I Ctrl+F'd through the class looking for appearances of "angle", the variable that you set to rotate a sprite, but all I can find is some obscure calculation in it's used to compute a frame number, and I can't find a single call to any function that looks like it's doing real-time bitmap rotating.



So, I was able to solve this problem. All FlxSprites hold the bitmap for their currently visible frame in an internal variable called framePixels. If you load a graphic using loadGraphic, then you can do real-time rotation: in this case, the draw() function rotate s framePixels "at the last minute", right before rendering it. Meaning the rotated bitmap is not stored, and FlxCollision has no way of seeing it. FlxCollision runs its collision checks against the unrotated bitmap.

So this is what need to be done:

  1. Get the sprite to store its rotated graphic somewhere rather than just sending it to the renderer and then abandoning it.
  2. Get FlxCollision to use that stored rotated graphic rather than framePixels.

This is summarized in the following diagram. Flixel's default logic is in black, my changes are in red.

Overview of code changes

I applied all of this in a sub-class of FlxSprite that I called PerfectCollisionSprite.

Here's my code.

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

The draw() function uses matrix rotation to rotate a non-baked FlxSprite. You can see the relevant section in the code block marked "Advanced render", line 456 of FlxSprite.as in the current version. That section already accounts for a different rotation origin, held in the "origin" variable.

What I'd do is extend the FlxSprite class. The pixels variable will hold your original lockpick image. Override the angle variable with a property that sets dirty=true each time you change the angle.

Now override calcFrame() so that the rotation happens there instead of in draw(). calcFrame() normally copies the correct frame from pixels to framePixels; instead, you're going to use something like the advanced render code from draw() to copy a rotated version of pixels to framePixels.

Of course, you also need to change draw() or apply some other fix so that it doesn't perform a second rotation in the aforementioned advanced render section.

I assume FlxCollision bases its pixel-perfect collision on framePixels, so that should make your collisions work right.

\$\endgroup\$
4
  • \$\begingroup\$ Not quite... I think I'm sort of getting the feel for it, though. FlxCollision does use framePixels. And as far as I can see, the real time rotation implemented by draw() doesn't change framePixels, it just creates a new BitmapData and renders that. So couldn't I just extend FlxSprite, override draw() and store that rotated BitmapData somewhere, then extend FlxCollision and get it to use that rather than framePixels? \$\endgroup\$
    – Jack
    May 9, 2012 at 16:31
  • \$\begingroup\$ Also, how can I display a BitmapData on the screen, for debugging purposes? \$\endgroup\$
    – Jack
    May 9, 2012 at 17:05
  • \$\begingroup\$ I edited my answer to add more detail. Note that FlxSprite's draw() function renders to the cameras' buffers, not an arbitrary new BitmapData; you could draw or stamp onto some placeholder BitmapData, but that would probably involve two rotations instead of one. If you've only got one lockpick on screen at once, of course, that's fine. You can display a BitmapData any number of ways; one would be to add a Bitmap to the stage and set it to use the BitmapData, and another would be to set another FlxSprite's "pixels" variable to that BitmapData (and use drawframe() to force a redraw). \$\endgroup\$ May 9, 2012 at 17:10
  • \$\begingroup\$ Okay! I just got it working, finally. It's not the most elegant code, but I can restructure it later. Thanks a lot, your help definitely pushed me in the right direction. I'll edit my solution into my question for future readers once I get the final kinks worked out (and once I get some sleep, lol). \$\endgroup\$
    – Jack
    May 11, 2012 at 6:11

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .