animationcocos2d-iphonexcode4.2ccsprite

Sprite Sheet animation fails after upgrading from Cocos2d 1.0.1 to 2.0


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.


Solution

  • Here's the code I ended up with that works correctly. Not sure why I had to make these changes, but there it is. (Some of the names have changed as I started a new project to fix this on, but the differences between my original code and this are apparent). To anyone else finding this thread, my original code was based on Ray Wenderlich's sprite sheet tutorial.

    CCSpriteBatchNode *chameleonBN = [CCSpriteBatchNode batchNodeWithFile:@"chameleonimages.png"];
        [self addChild:chameleonBN];
    
    //ADDED the texture part to resolve: 'CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode'   
    [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:@"chameleonplist.plist" texture:chameleonBN.texture];
    
    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.9f];
    
    //ADDED this sprite frame to resolve texture id assert error
    CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:chameleonBN.texture rect:CGRectMake(0, 0, 149, 122)];
    CCSprite *chameleon = [CCSprite spriteWithSpriteFrame:frame];
    chameleon.position = ccp(512,384);
    CCAnimate *chameleonAnimate = [CCAnimate actionWithAnimation:mouthAnim];
    
    CCDelayTime *chameleonDelay = [CCDelayTime actionWithDuration:10];
    CCDelayTime *chameleonDelay2 = [CCDelayTime actionWithDuration:0.1];//Had to use this to ge tthe mouth to close. Using restore original frame doesn't work for me.
    CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAnimate, chameleonDelay2, nil]]; 
    [chameleon runAction:chameleonRepeat];
    [chameleonBN addChild:chameleon];