Animation finishes on first frame - iphone

I play an animation like this:
- (CCSprite *)explodeWithBatchNode:(CCSpriteBatchNode *)batchNode andAnimation:(CCAnimation *)ani
{
CCSprite *explosionSprite = [CCSprite spriteWithTexture:batchNode.texture rect :CGRectMake(0, 0, 154,119)];
CCAction *goldenExplosionAction = [CCAnimate actionWithAnimation:ani];
[explosionSprite runAction:goldenExplosionAction];
return explosionSprite;
}
Everything works fine, the only problem is that the animation stops at the first frame instead of the last. How can I have it finish on the last one?, also, how can I know that the animation has finished?

+ (id) actionWithAnimation:CCAnimation *) a restoreOriginalFrame:(BOOL) b
it's a method of a CCAnimate action. Just put the second argument to NO
In your case
CCAction *goldenExplosionAction = [CCAnimate actionWithAnimation:ani restoreOriginalFrame:NO];
To understand the animation was finished create a callback, that will be called after animation like this:
id animation = [CCAnimate actionWithAnimation: someAnimation];
id callback = [CCCallfunc actionWithTarget: targetObject selector: #selector(mySelector)];
id sequence = [CCSequence actions: animation, callback, nil];
[mySprite runAction: sequence];
So after animation is finished targetObject's mySelector method will be called.
If you want to pass some data through a callback and/or a sender use CCCallFuncN or CCCallFuncND actions

Related

Play animation after another animation on sprite

I've looked all over and i can't find any information on how to do this. I want to run an action animation then right after that animation is done i want it to run another action animation all on the same sprite. how is this possible?
[self.mainShip runAction:retractdoor];
[self.mainShip runAction:activatedoor];
this crashes me.
self.mainShip runAction: [CCSequence actions:retractdoor,activatedoor, nil];
gives me a yellow notification
incompatible pointer types sending Cc action to parameter of type ccfinite time action
CCAnimation *retractdoorAnimation = [CCAnimation
animationWithSpriteFrames:retractdoorframes delay:0.1f];
CCAnimation *activatedoorAnimation = [CCAnimation
animationWithSpriteFrames:activatedoorframes delay:0.1f];
self.retractdoorAction = [CCAnimate actionWithAnimation:retractdoorAnimation];
self.activatedoorAction = [CCAnimate actionWithAnimation:activatedoorAnimation];
you missed the nil termination.
[self.mainShip runAction: [CCSequence actions:retractdoor,activatedoor, nil]];
this should work, dont use the square brackets and dont miss the comma..

cocos2d dynamically changing animation speed with CCSpeed

In a simple game, a character has a walking animation of around 15 frames. Based on the accelerometer is speed in either direction changes. I wanted the animation to speed up or slow down accordingly. I realize that once the animation is initialized, you can't change the delay speed, and instead you need to create a new animation. But since the speed changes almost constantly, if I kept reseting the animation, it would always be stuck on the same frame.
Is there a way to find which frame an animation is currently in, stop it, then create a new animation with the same frame but starts at which the last animation ended?
Otherwise does anybody have an idea of how you could go about implementing a dynamically changing animation delay time?
EDIT:
I have tried using CCSpeed to do this from an example in Learn Cocos2d 2. Here is the code:
CCRepeatForever *walkLeftRepeat = [CCRepeatForever actionWithAction:[CCAnimation animationWithSpriteFrames:walkLeftFrames]];
self.walkLeft = [CCSpeed actionWithAction:walkLeftRepeat speed:animationWalkingSpeed];
I get the following warning: Incompatible pointer types sending 'CCRepeatForever *' to parameter of type 'CCActionInterval *'
Here is the example I am following from:
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:rotateBy];
CCSpeed* speedAction = [CCSpeed actionWithAction:repeat speed:0.5f];
Second EDIT:
Then I tried this:
CCAnimation *walkLeftRepeat = [CCAnimation animationWithSpriteFrames:walkLeftFrames];
CCAnimate *temp = [CCAnimate actionWithAnimation:walkLeftRepeat];
CCAnimate*temp2 = [CCRepeatForever actionWithAction:temp];
self.walkLeft = [CCSpeed actionWithAction:temp2 speed:animationWalkingSpeed];
[_guy runAction:_walkLeft];
Final edit. This problem of it not showing up was fixed by adding a delay to ccanimation.
This doesn't give any error's but nothing happens.
Thanks!
No need to recreate the animation. Use a CCSpeed action with the CCAnimate action as its inner action. Then you can modify the CCSpeed action's speed property to speed up or slow down the animation.
The alternative is to advance frames yourself. It's easy to do, you only need a scheduled update and a timer that tells you when to change to the next frame. You speed up or slow down the animation by simply changing how much you add to the timer every frame. If the timer is > than a specific value you change frames and reset the timer to 0.
You can do it in Cocos2d Game:
-(void)preLoadAnim
{
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:#"ANIMATION_HERO"];
if(!animation)
{
CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache];
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=4;i++)
{
CCSpriteFrame *frame = [cache spriteFrameByName:[NSString stringWithFormat:#"HeroRun_%d.png",i]];
[animFrames addObject:frame];
}
animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.5f;
animation.restoreOriginalFrame = NO;
[[CCAnimationCache sharedAnimationCache] addAnimation:animation name:animName];
}
}
-(void)runHeroSlowMode
{
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:#"ANIMATION_HERO"];
animation.delayPerUnit = 1.0f;
CCAnimate *animAction = [CCAnimate actionWithAnimation:animation];
CCRepeatForever *runningAnim = [CCRepeatForever actionWithAction:animAction];
runningAnim.tag = kTagHeroRunAction;
[self stopActionByTag: kTagHeroRunAction];
[heroSprite runAction:runningAnim];
}
-(void)runHeroFastMode
{
CCAnimation* animation = nil;
animation = [[CCAnimationCache sharedAnimationCache] animationByName:#"ANIMATION_HERO"];
animation.delayPerUnit = 0.1f;
CCAnimate *animAction = [CCAnimate actionWithAnimation:animation];
CCRepeatForever *runningAnim = [CCRepeatForever actionWithAction:animAction];
runningAnim.tag = kTagHeroRunAction;
[self stopActionByTag: kTagHeroRunAction];
[heroSprite runAction:runningAnim];
}
I was also facing the same problem. In my game whenever I collect something my speed increases. at that time I paused my animation at the current frame changed its delay time and then resumed it.... Now its working

Starting CCAction at particular time

I want to start InFinite CCAction at particular time. I tried using CCSequence but it supports only finite time animation.
Any idea?
Best Regards,
Paras
Put the action that you want to repeat inside of a method. Then put this in your init method
[[CCScheduler sharedScheduler] scheduleSelector:#selector(myMethod) forTarget:self interval:10 paused:NO];
This will call myMethod after 10 seconds, however once inside myMethod you'll want to unschedule it. So my method should look something like this.
- (void) myMethod
{
[[CCScheduler sharedScheduler] unscheduleSelector:#selector(myMethod) forTarget:self];
CCMoveBy *move = [CCMoveBy actionWithDuration:3 position:ccp(75,0)];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:move];
[mySprite runAction:repeat];
}
The last two lines are what you need.
CCMoveBy* move = [CCMoveBy actionWithDuration:3 position:ccp(75,0)];
CCCallFuncO* shot = [CCCallFuncO actionWithTarget:self selector:#selector(shoot:) object:enemy];
CCSequence* sequ = [CCSequence actions:move,shot,nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequ];
[sprite runAction:repeat]; //sprite here

iphone cocos2d CCSequence of Action and CCParticleSystem animation

I´m new to Cocos2d. I´m trying to run two animations one after another.
The first one is:
CCAction *walkAction;
CCAnimation *walkAnim = [CCAnimation
animationWithFrames:walkAnimFrames delay:0.15f];
bear = [CCSprite spriteWithSpriteFrameName:#"normal1.png"];
walkAction = [CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO];
[bear runAction:walkAction];
[spriteSheet addChild:bear];
The second which I want to fire right after the first one is:
CCParticleSystem *killPigAnim = [CCParticleSystemPoint particleWithFile:#"killPigAnim.plist"];
[self addChild:killPigAnim];
How can I achive that when the second one is not an action but the CCParticleSystem object.
You can use the action CCCallFunc to either call the start method on the particle system or call a method in your class which starts the particle system.
i.e.
-(void) startParticles
{
//Start your particles
}
-(void) myOtherMethod
{
...
walkAction = [CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO];
CCCallFunc *callAction = [CCCallFunc actionWithTarget:self selector:#selector(startParticles)];
[bear runAction:[CCSequence actionWithActions:walkAction, callAction, nil];
...
}

Restarting an action in cocos2D for the iPhone

If I am using an action that does not repeat in cocos2D how do I restart that action?
I am using the code:
CCAnimate* action = [CCAnimate actionWithAnimation:myAnimation];
[mySprite runAction: action];
The action runs fine once, but when an event is triggered I want to be able to run the action again as long as it is finished, so I tried using this when the even is triggered.
if( [action isDone] ){
[mySprite runAction: action];
}
But this results in a crash. Anyone any idea what the correct way to do this is?
try preserving the action in an instance variable. In the header file have a pointer declared
CCAction* myAction;
then when the layer or sprite is initialized
myAction = [CCAnimate actionWithAnimation:myAnimation];
From what point on, whenever you want to call your action do
if( [action isDone] ){
[mySprite runAction: myAction];
}
I think the reason your app is crashing is because you are calling an action that only exists for the duration of the method in which it is initialized.
Im my game i use CCSequences (so i can use CCCallFunc to set/declare variables mid animation), all these CCSequences are stored as instance variables in my CCSprite subclass.
I have an idle animation that repeats for ever.
Whenever I want to 'jump' for instance i call
[self stopAllActions];
[self runAction:jumpSeq];
My jumpSeq is a CCSequence that plays a jump animation, and has a CCCallFunc at the end of the sequence that restarts the idle animation when it is done.
Hope this helps.
Further reading: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:actions_special?s[]=cccallfunc
Turns out I just wasn't retaining the action and the sprite must have been deleting it once it was done. So now my code is;
CCAnimate* action = [CCAnimate actionWithAnimation:myAnimation];
[mySprite runAction: action];
[action retain];
and then when i want it to run again;
if( [action isDone] ){
[mySprite runAction: myAction];
}
I had the same issue I declared CCAction* myAction in the header file but when i went to call it from another method i experienced a crash but like #Bongeh mentioned when using [myAction retain] it worked perfectly