Run CCAction on multiple sprites - cocos2d-iphone

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.

Related

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.

Move a whole animation sprite sheet

I have some sprite-sheet that i have to animate forever , and i would like to add it as a CCLayer
to my scene .
Later on , i have to move this whole animation sprite on the screen.
So, for example, i have some animation of a dog walking, from sprite sheet, this one is running forever. than i want to be able to move this dog on screen while animating.
What is the best way to do this ? (or the right way)
This is how i animate the frames :
CCSprite *boom;
boom = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"%#_00000.png",file]];
boom.position=touch;
[self addChild:boom];
NSMutableArray *animFrames = [NSMutableArray array];
for(int i = 0; i < 5; i++)
{
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:
#"%#_0000%i.png",file,i]];
[animFrames addObject:frame];
}
CCAnimation* boxAnimation = [CCAnimation animationWithSpriteFrames:animFrames delay:0.075f];
CCAnimate * boxAction = [CCAnimate actionWithAnimation:boxAnimation];
CCAction *call=[CCCallBlock actionWithBlock:^{[self removeFromParentAndCleanup:YES];}];
CCAction * sequence=[CCSequence actions:boxAction,[CCHide action],call,nil];
[boom runAction:sequence];
return self;
How would you move this whole thing ?
There are a few ways to do this. If you are not preocupied with collision detection, then one way would be to :
CGPoint egressPosition = ccp(0,0); // figure this out in your app
float moveDuration = 1.5f ; // whatever time you compute for desired speed and distance
id move = [CCMoveTo actionWithDuration:moveDuration position:egressPosition];
id spawn = [CCSpawn actions:sequence,move,nil];
[boom runAction:spawn];
otherwise, using your code as is
[self schedule:#selector(moveBox:)]; // optional, you could do this in update method
[boom runAction:sequence];
-(void) moveBoom:(CCTime) dt {
CGPoint newPosition;
delta = ccp(dt*speedX,dt*speedY); // crude , just to get the idea
newPosition = ccpAdd(boom.position,delta);
// here you can figure out collisions at newPosition before the collision
// and do whatever seems appropriate
boom.position = newPosition;
}

CCEaseIn etc inside CCSequence

How do you exactly use easing inside CCSequence?
Related to Using CCEaseOut together with CCSequence?
here is an example. This brings back my mapNode to the left border, for example when a LHS menu is popped out. Move duration and acceleration are computed as a function of the expected displacement :
- (void)setLeftClamp:(float)leftClamp {
_leftClamp = leftClamp;
CGPoint currentPosition = self.mapNode.position;
if (currentPosition.x > self.maxX) {
// ease right back in position
CGPoint delta = ccp (self.maxX - currentPosition.x, 0);
id move = [CCMoveBy actionWithDuration:[self moveDuration:delta] position:delta];
id ease = [CCEaseIn actionWithAction:move rate:[self moveAcceleration:delta]];
id delay = [CCDelayTime actionWithDuration:.1f];
id easeAndCenter = [CCSequence actions:ease, delay, [CCCallFunc actionWithTarget:self selector:#selector(onMoveComplete)], nil];
[self.mapNode runAction:easeAndCenter];
targetMapLocation_ = ccpAdd(self.mapNode.position, delta);
mapDisplacement_ = delta;
isMapMoving_ = YES;
}
}

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];
}

program crashes when removing shapes in spacemanager - cocos2d

i am using spacemanager with chipmunk physics for my game.
What i am trying to do is hit a ball to a pillar and make the ball disappear. i am recreating the ball in different location so the user can hit another pillar. i have the ball shape and ball sprite as member variable.
Here is the code for creating ball, pillar shapes etc.
ball = [smgr addCircleAt:cpv(1000,10) mass:0.5 radius:35];
ball->collision_type = kBallCollisionType;
ballSprite = [cpCCSprite spriteWithShape:ball file:#"head.png"];
//[ballSprite autoFreeShape];
[self addChild:ballSprite];
ballSprite.spaceManager = smgr;
//ballSprite.ignoreRotation = NO;
cpShape *dome = [smgr addRectAt:cpv(400,500) mass:1 width:400 height:100 rotation:0];
dome->collision_type = kRectCollisionType;
cpCCSprite *dome1 = [cpCCSprite spriteWithShape:dome file:#"001.png"];
[self addChild:dome1];
dome1.spaceManager = smgr;
cpShape *pillarone = [smgr addRectAt:cpv(300,300) mass:1 width:45 height:194 rotation:0];
pillarone->collision_type = kRectCollisionType;
cpCCSprite *pillar1 = [cpCCSprite spriteWithShape:pillarone file:#"004.png"];
[self addChild:pillar1];
pillar1.spaceManager = smgr;
cpShape *pillartwo = [smgr addRectAt:cpv(500,300) mass:1 width:45 height:194 rotation:0];
pillartwo->collision_type = kRectCollisionType;
cpCCSprite *pillar2 = [cpCCSprite spriteWithShape:pillartwo file:#"004.png"];
[self addChild:pillar2];
pillar2.spaceManager = smgr;
cpShape *staticground = [smgr addRectAt:cpv(510,25) mass:1 width:0 height:0 rotation:0];
cpCCSprite *staticground1 = [cpCCSprite spriteWithShape:staticground file:#"grass1-1024.png"];
[self addChild:staticground1 z:1 tag:0];
[smgr addCollisionCallbackBetweenType:kRectCollisionType
otherType:kBallCollisionType
target:self
selector:#selector(handleCollisionWithFragmentingRect:arbiter:space:)];
AND HERE IS THE CODE FOR COLLISION HANDLING.
- (void) handleCollisionWithFragmentingRect:(CollisionMoment)moment arbiter:(cpArbiter*)arb space:(cpSpace*)space{
if (moment == COLLISION_POSTSOLVE)
{
[self removeChild:ball->data cleanup:YES];
[smgr removeAndFreeShape:ball];
ball = [smgr addCircleAt:cpv(1000,10) mass:0.5 radius:35];
ball->collision_type = kBallCollisionType;
ballSprite = [cpCCSprite spriteWithShape:ball file:#"head.png"];
[self addChild:ballSprite];
ballSprite.spaceManager = smgr;
}
}
When the first ball hits the pillar it disappears fine. but for the second and some times third ball i pick and hit the pillar it crashes with error as follows.
Chipmunk warning: Cannot remove a constraint that was not added to the space. (Removed twice maybe?)
Failed condition: cpArrayContains(space->constraints, constraint)
i am not sure where i went wrong, can anyone help please.
Thanks.
I think you need to add teh ball to be removed to a "garbage" array, and not remove it on the handleCollisionWithFragmentingRect function. For all safety measures, include the
- (void) update : (ccTime) dt
{
for (cpShape *shape in _garbage)
{
// Remove shape
[self removeChild:shape->data cleanup:YES];
[smgr removeAndFreeShape:shape];
// Add new ball
ball = [smgr addCircleAt:cpv(1000,10) mass:0.5 radius:35];
ball->collision_type = kBallCollisionType;
ballSprite = [cpCCSprite spriteWithShape:ball file:#"head.png"];
[self addChild:ballSprite];
ballSprite.spaceManager = smgr;
}
...
}
And then you would change the handle function to this:
- (void) handleCollisionWithFragmentingRect:(CollisionMoment)moment arbiter:(cpArbiter*)arb space:(cpSpace*)space
{
if (moment == COLLISION_POSTSOLVE)
{
// SHOULDNT BE A DIRECT REFERENCE TO MEMBER VARIABLE BALL.
// SHOULD BE ADDED FROM INFORMATION WITHIN THE PARAMETERS OF THIS FUNCTION.
_garbage.addObject(ball);
}
}