0
votes

I am wondering if someone can explain me how to re-use animations in cocos2d? I was used to so-called usual way: I create a 'game object', load animations and after that simply call my animations like gameObj->idle(); or gameObj->walkToPoint();...

I understand that these helper methods should be defined by myself, and I also studied a lot of guides about CC2d and using animations in different ways, but these guides are too simple and don't shot real cases.

So at the moment I wrote method which loads animation from plist and png texture, adds this texture to parent layer and assigns first sprite to the 'Game Object' sprite. But I have some questions - I still can't find the way to play animations.

Situation is more difficult for me (but I know that this is usual case :) - animations should be assigned to 'actions' and there should be different types of actions. For example - idle action plays forever, death plays once and stays at the last frame and shoot action plays once and restores previous frame - idle's one.

To make things simple I'll show some basic code w\o complex checks and for..in loops (I wondering who decided store all frames in cc2d animation format as Array and load them with frame_%d, not as Dictionary with structure Animations->Idle->Frames Animations->Shoot->Frames, but this not main point :))

//These things are global
CCAnimation *idleAnim; 
CCAction * idleAction; // idle animation should be played forever 

CCAnimation *deathAnim;
CCAction * deathAction; // death animation should be played once and stop at last frame

CCAnimation *shootAnim; 
CCAction * shootAction; // shoot animation should be played once and idle frame restored


    // loading animations with simple for loops
    - (id)init {
  self = [super initWithColor:ccc4(255,255,255,255)];
if (self) {     

    [[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:
     @"player.plist"];
    CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode 
                                      batchNodeWithFile:@"player.png"];

    [self addChild:spriteSheet];

    NSMutableArray *idleAnimFrames = [NSMutableArray array];
    for(int i = 1; i <= 20; ++i) {
      [idleAnimFrames addObject:
       [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
        [NSString stringWithFormat:@"hero_idle01_%04i_Layer-%i.png", i-1, i]]];
    }

    idleAnim = [CCAnimation animationWithFrames:idleAnimFrames delay:0.1f];

    idleAction = [CCRepeatForever actionWithAction:
                   [CCAnimate actionWithAnimation:idleAnim restoreOriginalFrame:NO]];



    NSMutableArray *deathAnimFrames = [NSMutableArray array];
    for(int i = 0; i <= 30; ++i) {
      [deathAnimFrames addObject:
       [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
        [NSString stringWithFormat:@"hero_death01_%04i_Layer-%i.png", i, i+1]]];
    }

    deathAnim = [CCAnimation animationWithFrames:deathAnimFrames delay:0.1f];

    deathAction =  [CCAnimate actionWithAnimation:deathAnim restoreOriginalFrame:NO];





    NSMutableArray *shootAnimFrames = [NSMutableArray array];
    for(int i = 0; i <= 19; ++i) {
      [shootAnimFrames addObject:
       [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
        [NSString stringWithFormat:@"hero_idleshoot02_%04i_Layer-%i.png", i, i+1]]];
    }

    shootAnim = [CCAnimation animationWithFrames:shootAnimFrames delay:0.1f];

    shootAction = [CCAnimate actionWithAnimation:shootAnim restoreOriginalFrame:YES];



    self.player = [CCSprite spriteWithSpriteFrameName:@"hero_idle01_0000_Layer-1.png"];        

    self.player.position = ccp(20, self.size.height/2);

    [self.player runAction:shootAction];

    [spriteSheet addChild:self.player];

    [self.player runAction:[CCRotateTo actionWithDuration:0.0f angle:45]];



    [self schedule:@selector(gameLogic:) interval:1.0];
    }
    return self;
}

This code runs correctly (loads idle animation and plays it forever) but I can't find the way to switch between actions and animatinos nicely. I've seen approaches when for example when someone wan't shoot animation, he deletes original sprite takes first sprite from shoot, plays shoot, deleted shoot sprite, restores original sprite. But what if sprite was rotated? One should pass all the params from original sprite such as flip, rotation, scale, etc. back and forward between sprites? Usually Game Objects are inherited from CCSprite, so this method generally means that one should delete and restore main game object all the time? This is not clear for me.

I tried next things, but they didn't work for me, program just halts and nothing is written to debug (I guest it is because error happens in other thread, the one which is responsible for touch handling).

// somewhere after touch happened in [shootToPoint:(CGPoing)point] method
  [idleAction stop];  
  [shootAction startWithTarget:self.player];
  [self.player runAction:[CCSequence actions:
                            shootAction,
                             [CCCallFuncN actionWithTarget:self selector:@selector(shooted)],
                             nil]];

And in shooted method I call again something like this:

    [shootActino stop]; // or [self.player stopAllActions] - doesn't matter I guess
    [idleAction startWithTarget:self.player];
     CCSprite *sprite = (Bullet *)sender; // remove bullet sprite from memory
    [self removeChild:sprite cleanup:YES]; // remove bullet sprite from memory
    // other routines...

So can someone explain me how to create game objects with helper methods to switch animations? Please don't send me to cc2d docs because for it is unclear how to solve this simple problem from cc2d docs or other tutorial on the web.

1

1 Answers

2
votes

Since you are creating your instances with helper methods instead of alloc/initWith... they are getting autoreleased, that's why you get a crash.

For example your idle animation should be created like this:

NSMutableArray *idleAnimFrames = [[NSMutableArray alloc] init];
for(int i = 1; i <= 20; ++i) {
    [idleAnimFrames addObject:
     [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
      [NSString stringWithFormat:@"hero_idle01_%04i_Layer-%i.png", i-1, i]]];
}

CCAnimation *idleAnimation = [[CCAnimation alloc] initWithFrames:idleAnimFrames delay:0.1f];
[idleAnimFrames release];

CCAnimate *idleAnimate = [[CCAnimate alloc] initWithAnimation:idleAnimation restoreOriginalFrame:NO];
[idleAnimation release];

idleAction = [[CCRepeatForever alloc] initWithAction:idleAnimate];
[idleAnimate release];

That way you can use this action multiple times until you release it (which you should do in your dealloc method, or somewhere else when you're done with it).