I created a simple, 2 frame sprite animation based off a few tutorials that I found across the web. I took my 2 images, created a sprite sheet with plist and png file, and incorporated them into my code as seen below. This setup worked fine in Cocos2d V 1.0.1. I just upgraded my project to 2.0rc0a and now my app crashes with the following error at the point where it should switch from the first frame to the second: 'CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode'
I looked at this SO question, but I'm not sure if it's the same thing that I'm doing wrong, and because I'm still very new to Cocos2d, am unsure how to correctly adjust my code. Is this something that was changed in 2.0 that I didn't see in the notes, a bug that I should report, or just incorrect coding on my part? I still have a copy of the 1.0.1 project with identical code and the animation works correctly.
//CHAMELEON
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"front page/mainchameleon.plist"];
mainChameleon = [CCSpriteBatchNode batchNodeWithFile:@"front page/mainchameleon.png"];
[self addChild:mainChameleon z:7];
NSMutableArray *chameleonFrames = [NSMutableArray array];
for (int i = 1; i <= 2; ++i)
{
[chameleonFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:@"chameleon%d.png", i]]];
}
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonFrames delay:0.3f];
chameleon = [CCSprite spriteWithSpriteFrameName:@"chameleon1.png"];
CCAnimate *chameleonAction = [CCAnimate actionWithAnimation:mouthAnim];
CCDelayTime *chameleonDelay = [CCDelayTime actionWithDuration:10];
CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAction, chameleonDelay, nil]];
[chameleon runAction:chameleonRepeat];
[mainChameleon addChild:chameleon];
If I comment out chameleon = [CCSprite spriteWithSpriteFrameName:@"chameleon1.png"];
then the app doesn't crash, but the chameleon doesn't appear at all, as would be expected with how the code is currently written.
Or, if I comment out [chameleon runAction:chameleonRepeat];
then the chameleon appears showing the frame, chameleon1.png, but obviously doesn't go through the animation.
OK, so to make me even more confused because I'm definitely missing something, I tried changing the bottom portion of the code to this and the animation goes from frame 1, to frame 2, then stays on 2 indefinitely. However, if I make the delay anything longer than 1.0, I receive the same error I was getting before. If I re-include chameleonDelay
prior to the action without the repeat forever statement, I also get the same crash. It appears that the app crashes if it has to wait longer than 1 second to perform the switch. What I need is for frame 1 to sit for a while (10 seconds) then switch to frame 2 for 0.3 seconds, then switch back to frame 1 and sit for a while again.
Attempted code #2:
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonFrames delay:0.3f]; //<--- maxes out at 1.0. Anything more causes crash
chameleon = [CCSprite spriteWithSpriteFrameName:@"chameleon1.png"];
CCAnimate *chameleonAction = [CCAnimate actionWithAnimation:mouthAnim];
[chameleon runAction:chameleonAction];
[mainChameleon addChild:chameleon];
YvesLeBorg suggested using the restoreOriginalFrame statement, but that is deprecated in ver 2.0. I tried using
CCAnimation *mouthAnim = [CCAnimation animationWithAnimationFrames:chameleonFrames delayPerUnit:0.3f loops:5];
and get the error '-[CCSpriteFrame delayUnits]: unrecognized selector sent to instance'
. I'm not sure why that isn't working or what else to try from here.
EDIT: So Now It's Working...But not as efficiently coded as I'd like:
New Code:
//CHAMELEON
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"front page/mainchameleon.plist"];
mainChameleon = [CCSpriteBatchNode batchNodeWithFile:@"front page/mainchameleon.png"];
[self addChild:mainChameleon z:7];
NSMutableArray *chameleonFrames = [NSMutableArray array];
//Frame 1 - closed mouth
[chameleonFrames addObject:[CCSpriteFrame frameWithTexture:mainChameleon.texture rect:CGRectMake(0, 124, 149, 122)]];
//Frame 2 - Open Mouth
[chameleonFrames addObject:[CCSpriteFrame frameWithTexture:mainChameleon.texture rect:CGRectMake(0, 0, 149, 122)]];
//Frame 1 - closed mouth
[chameleonFrames addObject:[CCSpriteFrame frameWithTexture:mainChameleon.texture rect:CGRectMake(0, 124, 149, 122)]];
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonFrames delay:0.9f];
chameleon = [CCSprite spriteWithTexture:mainChameleon.texture rect:CGRectMake(0,124,149,122)];
CCAnimate *chameleonAction = [CCAnimate actionWithAnimation:mouthAnim];
CCDelayTime *chameleonDelay = [CCDelayTime actionWithDuration:10];
CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAction, nil]];
[chameleon runAction:chameleonRepeat];
[mainChameleon addChild:chameleon];
I really liked the way that I was doing it in 1.0.1 because if I had 2 frames or 100 frames, I only had to make a small adjustment to the if statement. This way requires coding each individual frame which seems counter-intuitive to using a plist. If no one is able to provide a better solution, or "real" answer in the next few days, I will post this as an answer and accept it to close out the question.