-
Notifications
You must be signed in to change notification settings - Fork 458
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
Conversation
Catch up with upstream.
FlxTilemapTest: add a basic test for loadMapFromGraphic()
Catch up with upstream.
Fix VCR to record simultaneous keys+mouse, closes HaxeFlixel#1739 (HaxeFlixel#1825)
Catch up with upstream.
Catch up with upstream.
Catch up with upstream.
Add function to get sound length.
Revert "Add function to get sound length."
…oop, if you play an animation that has a larger frame size than the previously playing one. (The loop assumes that there are more pixels than there are and crashes trying to read past the end of the "file".) Therefore they shouldn't be limited to renderTile, but should also work on flash/blit.
Definitely need a way of reproducing the issue. |
Any update on this one? In what situation does this fix a crash? |
As the commit says, on flash target, "if you play an animation that has a larger frame size than the previously playing one" and then do a collision check. I'll do up a test case now. |
Oh, right. I missed that commit comment. I'm a bit worried about this change because this code is fairly performance-critical, and doing two |
I suppose we could first check whether the frame size changed, and only run it then. |
Hmm, having trouble reproducing it. Very strange, I definitely needed this fix for my project. |
Ah, OK it's even a bit more specific, it seems. Got it breaking now. |
Here's a repro, I'm just turning it into a test case: package;
import flash.display.BitmapData;
import flash.geom.Matrix;
import flash.geom.Point;
import flash.geom.Rectangle;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.util.FlxCollision;
import flixel.util.FlxColor;
class PlayState extends FlxState{
override public function create():Void {
var sprite:FlxSprite = cast add(new FlxSprite(30, 0));
sprite.makeGraphic(100, 100, FlxColor.WHITE, true);
var sourceSprite = new FlxSprite();
sourceSprite.makeGraphic(100, 100, FlxColor.RED, true);
var animatedSprite:FlxSprite = cast add(new FlxSprite());
// load animations with varying frame sizes
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: 40, 0
index: -1
');
// add animations
for (name in ["a","b"])
animatedSprite.animation.addByPrefix(name, name);
// play smaller-framed anim
animatedSprite.animation.play("a");
trace(FlxCollision.pixelPerfectCheck(sprite, animatedSprite));
// play larger-framed anim
animatedSprite.animation.play("b");
// run PPC
trace(FlxCollision.pixelPerfectCheck(sprite, animatedSprite));
}
} |
Gee, it'd help if the test cases ran before my tests. How'd this get committed?
But even with that for some reason it's not working:
Am I running the wrong command? |
Er, OK, I try |
Try using these files: https://github.com/HaxeFlixel/flixel/tree/dev/tests/unit/targets They run |
Sorry, what command should I use? I got it completed with |
Missed that layer. Thanks! I guess also I was wrong earlier about confirming the passing, if it didn't regen the suite, it won't include my new test. |
Yeah, that'll surely fail. Forgot to finish the before section entirely. |
var animatedSprite:FlxSprite = cast add(new FlxSprite()); | ||
// play smaller-framed anim | ||
animatedSprite.animation.play("a"); | ||
trace(FlxCollision.pixelPerfectCheck(sprite, animatedSprite)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look like it should even compile...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hence 'that'll surely fail' :)
Nice having a working test suite so I can actually attempt to compile locally. I had looked for the docs on the haxeflixel.com site and seen nothing there, so I thought it was just assumed you know how to do it, and I had forgotten how. I forgot you can have nested readme.md's within a project and to look for that.
… case, but to avoid unnecessary paint() calls otherwise.
@@ -18,6 +18,7 @@ import flixel.tile.FlxTileblock; | |||
* @link http://www.photonstorm.com | |||
* @author Richard Davey / Photon Storm | |||
*/ | |||
@:access(flixel.FlxSprite) |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
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.
Feel free to file a new issue :) I almost never use neko, it seems to just require even more adjustments to all my function calls than flash does, and not provide much benefit to me over flash. |
Well, it's not exactly Neko-specific, looks the same on cpp... |
} | ||
|
||
@Test | ||
function pPCOnAnim() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pPCO
? What's that supposed to mean?
Ah, to be fair, it's bad input, I just noticed. I should probably change that... (the offset, size, and orig don't make sense together in b/0.) |
Although, even with that fixed, it still looks wrong. Try setting the offset to 30,0 and you'll see the resulting size is still 10x10 instead of 20x20. |
Different bug? :) |
@@ -36,7 +37,7 @@ b/0 | |||
xy: 90, 90 | |||
size: 20, 20 | |||
orig: 50, 50 | |||
offset: 40, 0 | |||
offset: 30, 0 |
There was a problem hiding this comment.
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.
If you comment out these lines, and just run drawFrame() no matter what, on my system it's about 9% slower.
It's quite odd I didn't notice that bug with my own project, you'd think that would've broken something... Anyway, hopefully Travis passes and this is all good now. |
I'm gonna double-check the performance implications of always running The current workaround is too hacky for my taste... (target-specific conditionals, but especially access to private fields...). If it's indeed necessary not to always run |
Maybe it's not target-specific but renderBlit-specific, like you mentioned in the other issue. I haven't tried this on html5. I suppose you could have it always run drawFrame() at this point in the code, and then make sure that animation.play() or whatever changes the current animation checks the frame size and marks the sprite dirty if the size changed. |
@Beeblerox Do you have an idea how to fix this in a less hacky way? |
if (FlxG.renderTile) | ||
#end |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
.
This needs a unit test, but in case anyone else was having flash crash on them during a collision check, before I get to that...