How to switch sprite animations in cocos2d-x?

I have a "GameCharacter" class where I have a sprite for my main character in the game, I loaded the frames and animations correctly. I can switch between animations with single touches or swiping gestures. Right now I can rotate between animations.

In my touch funtion of my scene I have this:

_mychar->RunAnimation(id);


where id is the ID of the animation, just an integer I change with every touch.

the method works like this:

void GameCharacter::RunAnimation(int a){

_sprite->stopAllActions();
if(a<_animation.size() && a>=0){
_sprite->runAction( CCRepeatForever::create(_animation[a]));
}
}


_animation[] is just a vector with CCAnimate* objects retained (I release them later when I destroy the GameCharacter).

I have 2 idle animations, I want to switch between them randomly, for example, I want to "queue" the first animation 3 to 5 times and the second 1 to 2 times, and repeat like this forever... also, I want to interrupt this "infinite random idle animation" with a touch that makes the character run another animation until it ends (for example a jump animation) and when it ends, just get to the idle animation. I don't know if I explained well, I hope you can understand me.

The id for the animations are:

• 0-idle1 (3 frames)
• 1-idle2 (6 frames)
• 2-jump (12 frames)
• 3-walk (12 frames)

Right now, i just need to be able to get the character in that idle state (random times between animations 0 and 1) and whenever touch, the jump animation is done once and at the end the idle start again...

I will solve the walking animation later.

First of all, I need to notify you that my answer needs a slight modification to your approach in RunAnimation() to be able to let this answer works.

Personally I don't recommend you to use that version of RunAnimation() function as it always executes the code with CCRepeatForever manner. My answer here can be applied with your case with a slight modification to your RunAnimation(). I recommend you to cache all animations, and actions in time of loading/creating character phase then when you need to play them you play it in separate function ie. walk(), idle(). We can combine using them later.

Please note that I mean both of animation and actions. This will benefit for more complex animation handling similar to combo system for beat'em up game later on. It's cleaner. All in all this question is complex if we need to cover all cases and handle stuff properly, you will see why at Ideas section below.

Basically you have to utilize using CCRepeat, and CCSequence to create one combined action to be used for running animation. As always, keep CCRepeatForever away from other CCActions context. It should not be mingled around. I always treat CCRepeatForever as a special case (special care) I need to pay attention to.

First question

About playing 3-5 times of first idle animation, then 1-2 times of second idle animation then loop both of them endlessly, you can do this by using the similar code as follows.

CCAction *_cached_idleAction = CCRepeatForever::create(CCSequence::createWithTwoActions(CCRepeat::create(_cached_idle_anim_1, 5), CCRepeat::create(_cached_idle_anim_2, 3)));


Then you use

_cached_idleAction


void GameCharacter::idle() {
stopAllActions();
runAction(_cached_idleAction);
}


Second question

About running idle animation after jump and walk animation, this relates to some kind of following up approach. The difficulty comes because we shouldn't mingle CCRepeatForever with any other CCAction (to be clear it won't mix and match with CCSequence, so try to avoid combining these twos).

The solution is that you create a separate function to play idle animation like the following.

void GameCharacter::followUpAction(){
// simple case
idle();
}


This spans into 2 cases

1) Go to idle after non-repeat-forever action (jump)

CCSequence *_cached_jumpAction = CCSequence::createWithTwoActions(CCRepeat::create(_cached_jump_anim, 1), CCCallFunc::create(this, callfunc_selector(GameCharacter::followUpAction)));


Then you normally playing jump via jump() as follows.

void GameCharacter::jump() {
stopAllActions();
runAction(_cached_jumpAction);
}


After jump animation finishes playing, it will play idle animation endlessly.

2) Go to idle after repeat-forever action (walk)

Because you're walking (it's repeat-forever), you need to call

stopAllActions()


to stop it first, then call walk(). Thus it means there's no automatic-sense like in the 1st case. You need to detect it via code which time you need to make it stopped and then run another repeat-forever action subsequently.

Ideas

The above detail applies to normal and simple case. In order to cover all cases and handle things properly, we need to keep track of

• The current state of character in which action (or say animation) it's acting (or playing).

You may need to inject CCCallFunc in each action created for all character's actions in order to set its state whenever it plays such animation. You don't set the state when you call runAction() because the animation will play next frame and not the same time as setting state and also remember that we can chain different action (state) inside one action.

• previousAction and currentAction

stopAllActions() is not a cure. If you call this in the mid and intention to switch animation, you will probably have a high chance to stop other not related to animation ie. knockback translation due to hit-effect from enemies. Thus a character will freeze and such knockback effect won't properly apply. To solve this, we have to keep track of those two actions and stop it accordingly, not all.

• followUpAction() is a key
• Thanks @haxpor for your answer, I just tested what you proposed here and it wors perfectly for the idle animation (it is not random times between one and another, but I can manage to solve that later) but the jump action is not working, I mean, the character is in idle, I touch and call the jump() method and the animation works, but at the end, the game crashes... What am I doing wrong? the error I get is: Process 51363999 (HelloWorld) terminated SIGSEGV code=1 fltno=11 ip=0017c5a0(org.cocos2dx.HelloWorld.testDev_lloWorlds66c958cf@_ZN7cocos2d12CCDictionaryD1Ev+0xe7d8) ref=000000cc – nosmirck Aug 1 '13 at 20:43
• as soon the jump animation ends, the game crashes – nosmirck Aug 1 '13 at 20:44
• I guess it's because of calling stopAllActions() in followUpAction(). Please try to remove stopAllActions() in followUpAction() and see if it works. It might stop in the mid of running action as it's still in CCSequence. – haxpor Aug 2 '13 at 7:50
• @nosmirck I guess it's because no one retained that _cached_idle_animation. when you first call run action, that action is retained by scheduler, later when you stop all actions, it's released and therefor gets deleted. finally when the jump animation is finished, you are trying to run an action which was already deleted, and that'll cause access violation. – Ali1S232 Aug 2 '13 at 7:56
• @haxpor I got it working by implementing my own methods to start one animation and block the others with states and locks and it works well, maybe not the best solution, but it works for me, I will accept your solution and post my class later to help others – nosmirck Aug 9 '13 at 22:19