How to add your own completion block after runAction is done in cocos2d - cocos2d-iphone

I am new to cocos2d right now I have some action as following:
[self.rewardLabel runAction:[CCSequence actionOne:[CCFadeOut actionWithDuration:3.5] two:[CCHide action]]];
I want to do something after this sequence of actions is done. How can I achieve that since there is a 3.5s duration?

I am assuming cocos2d 2.2 for the above snippet. If you use the array constructor for CCSequence, you can build some complex stuff. Here is an example (from my code) of a sequence with a call block :
id scaleDown = [CCScaleTo actionWithDuration:.25 scaleX:0.f scaleY:1.f];
id scaleUp = [CCScaleTo actionWithDuration:.25 scaleX:1.f scaleY:1.f];
id flip = [CCCallBlock actionWithBlock:^{ _normal.flipX=YES;}];
id unflip = [CCCallBlock actionWithBlock:^{_normal.flipX=NO;}];
id once = [CCSequence actions:scaleDown,flip,scaleUp,unflip,nil];
id twice = [[once copy] autorelease];
id three = [[once copy] autorelease];
id four = [[once copy] autorelease];
id done = [CCCallBlock actionWithBlock:^{
_normal.anchorPoint=ccp(0,0);
_normal.position=ccp(0,0);
}];
id all = [CCSequence actions:once,twice,three,four,done,nil];
[_normal runAction:all];

Related

How to use animations in cocos2d

I'm new to this and i'm making a game with SpriteBuilder and cocos2D (latest relase) but i cant manage to get animations working.
I have this code:
id scaleHorDown = [CCScaleTo actionWithDuration:duration 5/30.f scaleX:0.75f scaleY:1.0f];
id scaleHorBouncing = [CCEaseBounceIn actionWithAction:scaleHorDown];
id scaleVerDown = [CCScaleTo actionWithDuration:duration 5/30.f scaleX:1.0f scaleY:0.65f];
id scaleVerBouncing = [CCEaseBounceInOut actionWithAction:scaleVerDown];
id shrink = [CCSequence actions:scaleHorBouncing,scaleVerBouncing, nil];
id swell = [CCScaleTo actionWithDuration: duration * 15/30.f scale:1.10f];
id swellEase = [CCEaseElasticOut actionWithAction:swell];
id resetScale = [CCScaleTo actionWithDuration:duration * 5/30.f scale:1.0f];
id resetScaleBouncing = [CCEaseInOut actionWithAction:resetScale];
id buttonAction = [CCSequence actions: shrink, swellEase, resetScaleBouncing, nil];
[self runAction:buttonAction];
But it doesnt recognize most of the methods. I guess its a problem with the imports but i've tried a lot of things and nothing...
Thanks!

How to change ZOrder in a CCSequence?

I would like to know how to change the ZOrder of a CCSprite in a CCSequence. I have tried using CCCallBlock as suggested in another thread but it has bugged out and stops the other sprites of the same class moving. Is there another method someone can suggest?
crystalEntryPoint = [self position];
float xD = [crystal position].x - crystalEntryPoint.x;
float yD = [crystal position].y - crystalEntryPoint.y;
CGPoint dest = ccp(crystalEntryPoint.x + (xD * 2),crystalEntryPoint.y + (yD * 2));
float easingRate = 2;
CCMoveTo *moveTo = [CCMoveTo actionWithDuration:1 position:dest];
CCMoveTo *moveBack = [CCMoveTo actionWithDuration:1 position:crystalEntryPoint];
CCEaseInOut *easeTo = [CCEaseInOut actionWithAction:moveTo rate:easingRate];
CCEaseInOut *easeBack = [CCEaseInOut actionWithAction:moveBack rate:easingRate];
CCCallBlock *zOrderBehind = [CCCallBlock actionWithBlock:^{ [self setZOrder:kManaSpriteBehindCrystalZValue]; }];
CCCallBlock *zOrderInFront = [CCCallBlock actionWithBlock:^{ [self setZOrder:kManaSpriteZValue]; }];
CCSequence *seq = [CCSequence actions:easeTo,zOrderBehind,easeBack,zOrderInFront,nil]; //,zOrderInfront
CCRepeatForever *rep = [CCRepeatForever actionWithAction:seq];
[self runAction:rep];
Try CCCallBlockN which gives the block the node running the action as parameter. The current setup retains self inside the block which may cause the node not to dealloc later on because the sequence runs forever and thus holds a strong self-reference until the node deallocates - I can't say if cocos2d is able to clean it up properly, it should but it might not.
Not sure if this is related to your issue or not but it seems to me a likely explanation considering everything else in the code snippet looks fine.
CCCallBlockN *zOrderBehind = [CCCallBlockN actionWithBlock:^(CCNode* node){
[node setZOrder:kManaSpriteBehindCrystalZValue];
}];
CCCallBlockN *zOrderInFront = [CCCallBlockN actionWithBlock:^(CCNode* node){
[node setZOrder:kManaSpriteZValue];
}];
If that doesn't work either then try CCActionTween which allows changing any property by name.
id zOrderChange = [CCActionTween actionWithDuration:0.0
key:"zOrder"
from:kManaSpriteZValue
to:kManaSpriteZValue];
I'm not sure if from / to having the same value will work. If it doesn't try using 0 as the from parameter and perhaps even increasing duration to some low value like 0.01.

put sprites hidden on screen,than fade them in

I need to put some CCsprites on screen that later on will be fade in to screen.
I cant hide them ,because the CCFade action will not work on hidden sprite , or a sprite with opacity=0 .
What i do is put them on screen and fade them out :
[colors[i] runAction:[CCFadeOut actionWithDuration:0]];
[self addChild:colors[i] z:0];
it turned out that fading out in zero time is not unseen, so they appear for a second the moment i add them to CCScene.
How would i put them on screen to be unseen, and than fade them in with CCFadeIn action ?
You can stack actions using a sequence. See the example below from one of my projects :
CCSprite *frame1 = [CCSprite spriteWithSpriteFrame:[frames objectAtIndex:0]];
frame1.flipX = self.flipX;
frame1.scale = self.scaling;
frame1.visible = NO;
frame1.opacity = 255;
frame1.rotation = self.rotation;
frame1.position = self.offset;
animation = [CCAnimation animationWithSpriteFrames:frames delay:(duration / self.numberOfFrames)];
id stall = [CCDelayTime actionWithDuration:delay];
id show = [CCShow action];
id animate = [CCAnimate actionWithAnimation:animation];
id hide = [CCHide action];
id clean = [CCCallBlock actionWithBlock:^{
[frame1 removeFromParentAndCleanup:YES];
}];
id enchiladas = [CCSequence actions:stall, show, animate, hide, clean, nil];
[node addChild:frame1 z:5];
[frame1 runAction:enchiladas];
Similar thing. I want to run an animation that will appear after a set delay time, then vanish and cleanup after itself when complete.
YOU can use sprite.opacity =0; initially
and in actions you can increase the opacity

fading frames in and out

I have a set of frames that I need to show in a sequence one after the other but instead of simply erasing the previous frame and drawing the next one, I need to fade out the previous frame and fade in the new one simultaneously.
What is the best way to achieve this in cocos2d?
- (void) showFirstSpriteWithFade
{
if( [m_sprites count] > 0 )
{
CCSprite* spriteToShow = [m_sprites objectAtIndex: 0];
[m_sprites removeObjectAtIndex: 0];
id showAction = [CCSequence actions: [CCFadeIn actionWithDuration: fadeInDuration],
[CCFadeOut actionWithDuration: fadeOutDuration],
[CCCallFunc actionWithTarget: self selector:#selector(showFirstSpriteWithFade)],
nil];
[spriteToShow runAction: showAction];
}
}
this will work if you wil store all your sprites in array m_sprites. in this case all sprites must be added to the parent to be shown one by one. you can improve this code if you want, for example, use just one sprite and change it's texture each time.
if you wanna show pictures forever, you can try something like this
- (void) showNextSpriteWithFade
{
m_shownSpriteIndex++;
if( m_shownSpriteIndex == [m_sprites count] )
{
m_shownSpriteIndex = 0;
}
CCSprite* spriteToShow = [m_sprites objectAtIndex: m_shownSpriteIndex];
id showAction = [CCSequence actions: [CCFadeIn actionWithDuration: fadeInDuration],
[CCFadeOut actionWithDuration: fadeOutDuration],
[CCCallFunc actionWithTarget: self selector:#selector(showNextSpriteWithFade)],
nil];
[spriteToShow runAction: showAction];
}

Run CCAction on multiple sprites

I have an array of sprites and run an action on each element. the issue I am having is that the last sprite is the only one that moves.
If I add 3 arrays and iterate through each and use the following
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:moveSequence];
[[row1 objectAtIndex:i] runAction:repeat];
it only move the last drawn sprite.
How does one run an action on every element in an Array?
I need the objects to move at the same time. So all sprites should run the action simultaneously. Is this possible with cocos2d
EDIT*****************
- (void) moveAliens
{
id left = [CCMoveBy actionWithDuration:10 position:ccp(-35, 0)];
id right = [CCMoveBy actionWithDuration:10 position:ccp(35, 0)];
id moveSequence = [CCSequence actions:left, [CCDelayTime actionWithDuration:20], right, [CCDelayTime actionWithDuration:20], nil];
id repeatMoveSequence = [CCRepeatForever actionWithAction:moveSequence];
for (int i = 0; i < [row1 count]; i++)
{
NSLog(#"i is %d", i);
//CCRepeatForever *repeat = [CCRepeatForever actionWithAction:moveSequence];
[[row1 objectAtIndex:i] runAction:repeatMoveSequence];
}
}
Thanks
Place:
left = [CCMoveBy actionWithDuration:10 position:ccp(-35, 0)];
right = [CCMoveBy actionWithDuration:10 position:ccp(35, 0)];
moveSequence = [CCSequence actions:left, [CCDelayTime actionWithDuration:20], right, [CCDelayTime actionWithDuration:20], nil];
repeatMoveSequence = [CCRepeatForever actionWithAction:moveSequence];
inside your for loop. And the variable declarations above it.
You can't use one CCAction for multiple CCNodes simultaneously.