Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FlxCollision: fix an animation-related crash with renderBlit #1928

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion flixel/util/FlxCollision.hx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import flixel.tile.FlxTileblock;
* @link http://www.photonstorm.com
* @author Richard Davey / Photon Storm
*/
@:access(flixel.FlxSprite)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, not a fan of this... Have you checked whether it makes a noticable performance difference to call drawFrame()? Thinking about it some more, it might actually not matter much, since it's a non-op if the sprite isn't dirty.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't done a performance test. Oh, I see...that's interesting, I had misread drawFrame()'s if statement. I guess in this case it's properly marked dirty when it needs to be, otherwise this wouldn't be fixing anything.

class FlxCollision
{
// Optimization: Local static vars to reduce allocations
Expand Down Expand Up @@ -98,12 +99,17 @@ class FlxCollision
matrixB.identity();
matrixB.translate(-(intersect.x - boundsB.x), -(intersect.y - boundsB.y));

#if flash
if (Contact._frame.sourceSize.x != Contact.framePixels.width || Contact._frame.sourceSize.y != Contact.framePixels.height
|| Target._frame.sourceSize.x != Target.framePixels.width || Target._frame.sourceSize.y != Target.framePixels.height)
#else
if (FlxG.renderTile)
#end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't need both checks:

#if flash
        if (Contact._frame.sourceSize.x != Contact.framePixels.width || Contact._frame.sourceSize.y != Contact.framePixels.height || Target._frame.sourceSize.x != Target.framePixels.width || Target._frame.sourceSize.y != Target.framePixels.height)
 #else
        if (FlxG.renderTile)
 #end

so we just call Contact.drawFrame(); and Target.drawFrame();.

drawFrame() (and following methods which are called by it) have checks for value of dirty flag, so if sprite doesn't need to update its frame graphic then it will skip this step.
The only perfomance problem is with animated sprites when animation could have series of the same frames: when animation changes the frame (even if it has the same index) then it automatically resets sprite's frame, whih set dirty to true

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems you're right, I couldn't notice any FPS decrease in the PixelPerfectCollision demo when just always calling drawFrame.

{
Contact.drawFrame();
Target.drawFrame();
}

var testA:BitmapData = Contact.framePixels;
var testB:BitmapData = Target.framePixels;

Expand Down
66 changes: 66 additions & 0 deletions tests/unit/src/flixel/util/FlxCollisionTest.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package flixel.util;

import flixel.FlxSprite;
import flixel.graphics.frames.FlxAtlasFrames;
import massive.munit.Assert;

class FlxCollisionTest extends FlxTest
{
var sprite:FlxSprite;
var sourceSprite:FlxSprite;
var animatedSprite:FlxSprite;

@Before
function before():Void
{
sprite = new FlxSprite(30, 0);
sprite.makeGraphic(100, 100, FlxColor.WHITE, true);
sourceSprite = new FlxSprite();
sourceSprite.makeGraphic(100, 100, FlxColor.RED, true);
animatedSprite = new FlxSprite();
// load animations with varying frame sizes, such that the second has a bigger original size and offsets such that it will overlap with the white sprite
animatedSprite.frames = FlxAtlasFrames.fromLibGdx(sourceSprite.pixels, '
test.png
size: 100,100
format: RGBA8888
filter: Nearest,Nearest
repeat: none
a/0
rotate: false
xy: 0, 0
size: 10, 10
orig: 10, 10
offset: 0, 0
index: -1
b/0
rotate: false
xy: 90, 90
size: 20, 20
orig: 50, 50
offset: 30, 0
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to mention this in commit msg. Adjust so this is legal input.

index: -1
');
// add animations
for (name in ["a", "b"])
animatedSprite.animation.addByPrefix(name, name);

destroyable = sprite;
}

@Test
function pixelPerfectCheckWithAnim()
{
// play smaller-framed anim
animatedSprite.animation.play("a");
Assert.isFalse(FlxCollision.pixelPerfectCheck(sprite, animatedSprite));
}

@Test
function pixelPerfectCheckWithAnimAfterPlayingBiggerWithOffset()
{
// play larger-framed anim
animatedSprite.animation.play("b");
// run PPC
Assert.isTrue(FlxCollision.pixelPerfectCheck(sprite, animatedSprite));
}
}