How to add multiple sprites with time interval of 3 seconds and run same CCAction for all in cocos2d - cocos2d-iphone

Please help me as am fed with the searches and new to cocos 2d. Only am getting is the last sprite got moving if i schedule addRed and i want all the sprites moving randomly in the screen. Any help will be Appreciated and thanks in advance.
-(void)addRed
{
redSprite = [CCSprite spriteWithImageNamed:#"Red.png"];;
CGSize screenSize = [[CCDirector sharedDirector] viewSize];
[redSprite setPosition:CGPointMake(screenSize.width/2, screenSize.height/2)];
[self resizeSprite:redSprite toWidth:80 toHeight:80];
[self addChild:redSprite z:10];
[self gameStart];
}
- (void)gameStart {
// Create the actions
CGSize result = [[UIScreen mainScreen] bounds].size;
CGPoint nextPoint;
if (redSprite.position.x == result.width-40.0) {
nextPoint.x = redSprite.position.x - kAccelXAxis;
backx = YES;
}
else {
if (redSprite.position.x == 40.0) {
nextPoint.x = redSprite.position.x + kAccelXAxis;
backx = NO;
}
else {
if (backx) {
nextPoint.x = redSprite.position.x - kAccelXAxis;
}
else
{
nextPoint.x = redSprite.position.x + kAccelXAxis;
}
}
}
if (redSprite.position.y == 40.0) {
nextPoint.y = redSprite.position.y + kAccelYAxis;
backy = YES;
}
else {
if (redSprite.position.y == result.height-40.0) {
nextPoint.y = redSprite.position.y - kAccelYAxis;
backy = NO;
}
else {
if (backy) {
nextPoint.y = redSprite.position.y + kAccelYAxis;
}
else
{
nextPoint.y = redSprite.position.y - kAccelYAxis;
}
}
}
CCAction *myAction = [CCActionSequence actions:[CCActionMoveTo actionWithDuration:0.01 position:nextPoint], [CCActionCallFunc actionWithTarget:self selector:#selector(gameStart)], [CCActionCallFunc actionWithTarget:self selector:#selector(updateCol:)],nil];
[myAction setTag:10];
[redSprite runAction:myAction];
}

Try creating a updater with an interval of 3 sec for creating your sprites. For your created sprites, use a CCRepeatForever which will generate your next positions for your sprite.
For your case, I also thought about using a custom CCSprite to store your next position to move.
You will need something like...
[self schedule:#selector(createSprite) interval:(3)]; //to schedule your sprite generator
// Your sprite generator with action
-(void)createSprite{
CCCustomRed *red = [CCCustomRed spriteWithFile:#"red.png"];
[self addChild:red];
CCCallFuncN *changePos = [CCCallFuncN actionWithTarget:self selector:#selector(setRandomPos:)];
CCMoveTo *move = [CCMoveTo actionWithDuration:1 position:red.nextPosition];
CCDelayTime *delay = [CCDelayTime actionWithDuration:1.0];
[red runAction:[CCRepeatForever actionWithAction:[CCSequence actions:move,changePos,delay,nil]]];
}
//Generate a random pos as your sprite next move
-(void)setRandomPos:(id)sender{
CCCustomRed *red = (CCCustomRed *) sender;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
red.nextPosition = ccp(arc4random()%screenSize.width,arc4random()%screenSize.height);
}
Hope it helps :)

Related

progress time bar in cocos2d disappers after restart game

I've created a quiz game, that implements a time bar. In the first play it's all right, but if, after gameover, the player tap "restart", the game goes on properly, but the time bar disappears!
Here my code from GameOverLayer to Game:
-(void) restart {
[[CCDirector sharedDirector] replaceScene:[HelloWorldLayer node]];
}
Here the function to create a new question
-(void)creaDomanda{
//bar
CCProgressFromTo *to1 = [CCProgressFromTo actionWithDuration:MaxTime from:100 to:0];
bar = [CCProgressTimer progressWithFile:#"barra.png"];
bar.type = kCCProgressTimerTypeHorizontalBarLR;
[bar setPosition:ccp(size.width - 250 , size.height - 18)];
int randomValue = (arc4random() % 4) + 1;
NSString *stringa = [NSString stringWithFormat:#"Domanda%i", randomValue];
dictionary = [plistData objectForKey:stringa];
domanda = [dictionary valueForKey:#"Titolo"];
labelDomanda = [CCLabelTTF labelWithString:domanda fontName:#"Marker Felt" fontSize:24];
labelDomanda.position = ccp( size.width /2 , 400 );
[self addChild: labelDomanda];
int rispostaEsatta = [[dictionary valueForKey:#"Soluzione"] intValue];
menu = [CCMenu menuWithItems:nil];
for (int i = 1; i<5;i++)
{
if(rispostaEsatta == i){
item = [CCMenuItemFont itemFromString:[dictionary valueForKey:
[NSString stringWithFormat:#"Risposta%i",i] ]
target:self selector:#selector(corretto)];
}else{
item = [CCMenuItemFont itemFromString:[dictionary valueForKey:
[NSString stringWithFormat:#"Risposta%i",i] ]
target:self selector:#selector(sbagliato)];
}
[menu addChild:item];
}
//[..]
[self addChild:menu];
[self addChild:bar];
[bar runAction:to1];
}
And here one of the correct/wrong method (similar) that after all, create a new question:
-(void)sbagliato{
CCLOG(#"Sbagliato");
if (menu) [self removeChild:menu cleanup:YES];
if (labelDomanda) [self removeChild:labelDomanda cleanup:YES];
if (bar) [self removeChild:bar cleanup:YES];
labelRisultato = [CCLabelTTF labelWithString:#"Hai sbagliato!" fontName:#"Marker Felt" fontSize:24];
[labelRisultato setColor:ccc3(255, 1, 1)];
labelRisultato.position = ccp(size.width / 2, 280);
[self addChild:labelRisultato];
[self gameOver:2 punteggio:0];
// Richiamiamo il metodo per eliminare la label dopo 0,3 secondi
[self performSelector:#selector(eliminaLabel) withObject:nil afterDelay:0.5];
increment = increment - 20;
[pointLabel setString: [NSString stringWithFormat: #"Punti: %i", increment]];
// new question
[self performSelector:#selector(creaDomanda) withObject:nil afterDelay:0.5];
}
Can anyone explain to me please why when I restart the time bar desappers?
Thank You
My best guess:
The CCProgressFromTo action is still running. Since it progresses down to 0, the CCProgressTimer eventually doesn't display any part of it anymore. This may continue even if you run another CCProgressFromTo action on the progress timer.
Solution: be sure to stop any running CCProgressFromTo actions before running another.
If that doesn't fix it, then I imagine the CCProgressTimer needs to be reset by setting percentage back to 100.

Cocos2d. Rotate point around another point without hard math calculations?

I haven't seen easy examples about rotaion around a specified point. I tried something like this but it doesn't work:
//CCNode *node is declared
//In a function of a subclass of CCSprite
- (void)moveWithCicrlce
{
anchorNode = [CCNode node];
anchorNode.position = ccpSub(self.position, circleCenter);
anchorNode.anchorPoint = circleCenter;
[anchorNode runAction:[CCRotateBy actionWithDuration:1 angle:90]];
[self runAction:[CCRepeatForever actionWithAction:[CCSequence actions:[CCCallFunc actionWithTarget:self selector:#selector(rotate)], [CCDelayTime actionWithDuration:0.1], nil]]];
}
- (void)rotate
{
self.position = ccpAdd(anchorNode.position, anchorNode.anchorPoint);
}
Here's how you can rotate a node (sprite etc) around a certain point P (50,50) with a radius (distance from P) of 100:
CCNode* center = [CCNode node];
center.position = CGPointMake(50, 50);
[self addChild:center];
// node to be rotated is added to center node
CCSprite* rotateMe = [CCSprite spriteWithFile:#"image.png"];
[center addChild:rotateMe];
// offset rotateMe from center by 100 points to the right
rotateMe.position = CGPointMake(100, 0);
// perform rotation of rotateMe around center by rotating center
id rotate = [CCRotateBy actionWithDuration:10 rotation:360];
[center runAction:rotate];
My approximate solution:
#interface Bomb : NSObject {
CCSprite *center;
}
...
#end
and some methods:
- (void)explode
{
BombBullet *bullet = [BombBullet spriteWithFile:#"explosion03.png"];
[[[CCDirector sharedDirector] runningScene] addChild:bullet];
center = [CCSprite spriteWithTexture:bullet.texture];
center.position = explosionPoint;
center.anchorPoint = ccp(-0.5, -0.5);
center.visible = NO;
[[[CCDirector sharedDirector] runningScene] addChild:center];
[center runAction:[CCRotateBy actionWithDuration:1 angle:360]];
CCCallFunc *updateAction = [CCCallFuncN actionWithTarget:self selector:#selector(update:)];
[bullet runAction:[CCRepeatForever actionWithAction:[CCSequence actions:updateAction, [CCDelayTime actionWithDuration:0.01], nil]]];
}
- (void)update:(id)sender
{
BombBullet *bombBullet = (BombBullet *)sender;
bombBullet.rotation = center.rotation;
bombBullet.position = ccpAdd(center.position, center.anchorPointInPoints);
bombBullet.position = ccpAdd(bombBullet.position, ccp(-bombBullet.contentSize.width / 2, -bombBullet.contentSize.height / 2));
bombBullet.position = ccpRotateByAngle(bombBullet.position, center.position, bombBullet.rotation);
}
of course I should add sprite deleting.

Weird bug in my Cocos2d Game

I am done making a very simple Cocos2d educational game for kids. The game shows the user bubbles with letter or number and a voice tells the user to pop the particular letter or alphabet. The game works fine most of the time but sometimes the game says "Pop the Letter R". and then when you try to touch and pop it does not pop. This happens 10% of the time which is preventing me to submit the app to app store. I am not really sure where I am missing.
The method that selects random alphabets or numbers:
The options in the below code can be a NSMutableArray of which consists of alphabets from A to Z.
-(void) populateRandomBubbles:(NSMutableArray *) options
{
int randomNumber;
int randomBubbleColor;
NSString *option = #"";
for(int i=0;i<self.gamePlaySettings.noOfBubblesToDisplay;i++)
{
randomNumber = arc4random() % options.count;
randomBubbleColor = arc4random() % self.bubbleColors.count;
option = [options objectAtIndex:randomNumber];
CGPoint point = [self getRandomPointOnScreen];
// add first bubble
Bubble *bubble = [[Bubble alloc] initWithColor:[self getRandomBubbleColor]];
bubble.delegate = self;
bubble.text = option;
bubble.soundFile = [[option stringByAppendingPathExtension:#"caf"] lowercaseString];
bubble.sprite.position = point;
bubble.tag = [option characterAtIndex:0];
[bubble setStringForLabel:bubble.text];
if([self getChildByTag:bubble.tag] == nil)
{
if( (int) (self.bubbles.count) < self.gamePlaySettings.noOfBubblesToDisplay)
{
[self.bubbles addObject:bubble];
}
}
else
{
i--;
}
}
// set the randomly selected alphabet
randomNumber = arc4random() % self.bubbles.count;
Bubble *bubble = [self.bubbles objectAtIndex:randomNumber];
bubble.isSelected = YES;
// play sound
[self.environment playSoundByFileName:bubble.soundFile];
}
The Bubble class is defined below:
#implementation Bubble
#synthesize soundFile,text,isSelected,color,label,delegate = _delegate;
-(id) initWithColor:(NSString *)bubbleFile
{
self = [super init];
self.sprite = [CCSprite spriteWithFile:bubbleFile];
[self addChild:self.sprite];
return self;
}
-(void) pop
{
CCParticleExplosion *explosion = [[CCParticleExplosion alloc] init];
explosion.position = self.sprite.position;
[explosion setAutoRemoveOnFinish:YES];
[self.parent addChild:explosion];
[self.parent removeChild:self cleanup:YES];
Environment *environment = [[Environment alloc] init];
[environment playPopSound];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if([self containsTouchLocation:touch])
{
// if this is the correct bubble then pop the bubble
if(self.isSelected)
{
[self pop];
[_delegate onBubblePop:self];
}
return YES;
}
return NO;
}
The BaseNode is defined below:
#implementation BaseNode
#synthesize sprite;
-(void) onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[super onEnter];
}
-(void) onExit
{
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
- (CGRect)rect
{
CGSize s = [self.sprite.texture contentSize];
return CGRectMake(-s.width / 2, -s.height / 2, s.width, s.height);
}
- (BOOL)containsTouchLocation:(UITouch *)touch
{
BOOL isCollided = CGRectContainsPoint([self.sprite boundingBox], [self convertTouchToNodeSpace:touch]);
return isCollided;
}
Any ideas where the bug is?
UPDATE 1:
The following code which is also pasted in the original makes sure that there are no duplicates. It seems to be working fine since I have never encountered any duplicate.
if([self getChildByTag:bubble.tag] == nil)
{
if( (int) (self.bubbles.count) < self.gamePlaySettings.noOfBubblesToDisplay)
{
[self.bubbles addObject:bubble];
}
}
Try using this to ensure that you do not have any duplicates
int length = [YOURBUBBLEARRAY count];
NSMutableArray *indexes = [[NSMutableArray alloc] initWithCapacity:length];
for (int i=0; i<length; i++) [indexes addObject:[NSNumber numberWithInt:i]];
NSMutableArray *shuffle = [[NSMutableArray alloc] initWithCapacity:length];
while ([indexes count])
{
int index = rand()%[indexes count];
[shuffle addObject:[YOURBUBBLEARRAY objectAtIndex:index]];
[indexes removeObjectAtIndex:index];
}
[indexes release];
Bubble *bubble = [shuffle objectAtIndex:randomNumber];
Hope that helps
I might have solved the problem. I think the problem was that I was never making a check to ensure that the objects in the bubbles collection are unique. The updated code is below:
NSArray *bubbleAlreadyInArray = [self.bubbles filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"tag == %d",bubble.tag]];
if([self getChildByTag:bubble.tag] == nil)
{
if( (int) (self.bubbles.count) < self.gamePlaySettings.noOfBubblesToDisplay)
{
// only add to the bubbles array if not already added!
if(bubbleAlreadyInArray.count == 0)
{
[self.bubbles addObject:bubble];
}
}
}
else
{
i--;
}
I wonder if there is a better way than using NSPredicate but for now it works fine and does not get duplicates.
The game works fine most of the time but sometimes the game says "Pop the Letter R".
The question is... are you popping the correct letter r? There's nothing in your code that avoids using duplicates. It's possible that there are two r's on the screen at the same time, and pressing one of them isn't triggering the action on the other.
Bubble *bubble = [self.bubbles objectAtIndex:randomNumber];
isn't setting both bubbles if there are duplicates.
it's actually here...
[self.parent removeChild:self cleanup:YES];
needs to be
[self.parent removeChild:self.sprite cleanup:YES];

Animations are not working properly(Cocos2d)

My problem is that animation are not working properly during movements of sprite.
Below is the code which i'm using
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
[selSprite resumeSchedulerAndActions];
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self selectSpriteForTouch:touchLocation];
return TRUE;
}
- (void)selectSpriteForTouch:(CGPoint)touchLocation
{
CCSprite * newSprite = nil;
for (CCSprite *sprite in movableSprite) {
if (CGRectContainsPoint(sprite.boundingBox, touchLocation)) {
newSprite = sprite;
break;
}
}
if (newSprite != selSprite) {
[selSprite stopAllActions];
selSprite = newSprite;
_MoveableSpritetouch = TRUE;
}
if(_MoveableSpritetouch==TRUE)
{
movement=0;
CGRect selRect=CGRectMake((SpriteX)-20.0,(SpriteY)-20.0,40.0,40.0);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
}
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if(gameState == TRUE){
CGPoint point = [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
if (moveDifference.x>0)
{
selSprite.flipX = YES;
}
else {
selSprite.flipX = NO;
}
[selSprite setPosition:point];
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
movement=0;
if(selSprite==MarshallCar)
{
[selSprite setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"mcar_idle.png"]];
}
[selSprite pauseSchedulerAndActions];
}
The animation frames for movement are not playing every time during movements sometimes it plays or sometimes not. It plays properly when you touch and move your sprite for the first time but if touch another sprite and then again move the previous sprite the animations for movement won't play.
Is anyone having any idea why this is happening?
Please tell me the proper code for removing this bug.
Thanks!!!
I believe your problem is the if/else if construct:
if (_MoveableSpritetouch==TRUE)
{
CGRect selRect = CGRectMake(SpriteX - 20, SpriteY - 20, 40, 40);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
else if(...)
{
...
[selSprite runAction:walkMarshallCar];
}
}
If you don't see it right away: if the touch location is inside the selRect, you call stopAllActions on the selected (new) sprite and do nothing else. Only if the touch location is not within that rectangle you'll run the animation action.
I think the "in rectangle" check is superfluous since you've already called stopAllActions anyway a few lines above.
Allow me a few general remarks about your code:
The method "selectSpriteForTouch" tells me that you're selecting a new sprite. The function does that. But it does not advertise playing an animation. You might want to refactor this out to a seperate "playAnimationOnSelectedSprite" method.
You wrote 20.0 and 40.0 several times. This means you're actually passing a double (8 bytes floating point data type) to a CGPoint which takes floats (4 bytes floating point). Strictly speaking use either 20.0f with the suffixed "f" to denote it as a floating point data type, or use integers since you don't use the floating point part.
Why you put (SpriteX) in brackets is not clear to me, if you want to enhance readability you'll achieve more by adding spaces after commas and operands.
In Objective-C, use YES and NO macros instead of TRUE and FALSE.
The bool variable _MoveableSpritetouch seems superfluous, unless needed someplace else. In any case you should move the following if(_MoveableSpritetouch==TRUE) block to where you set the _MoveableSpritetouch variable to TRUE because it just makes your code harder to read by setting a variable, leaving the code block you were in ( if(selSprite != newSprite) ) just to jump into another block of code ( if(_MoveableSpritetouch==TRUE) ) that you already know you're going to run anyway.
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
I have added [selSprite stopAllActions];
and it started working correctly because in the touch ended method i was pausing the actions
but not resuming them so when i touch the sprite for the second time it was not playing animation because the action was paused.

On CCBitmapFontAtlas effect (Display on by one)

I'm trying to make a effect using the CCBitmapFontAtlas, here is what I want:
The string say "ABCDEFG" being dispayed one by one, each one won't be displayed
until the one before is completely displayed.
And here is what I tried:
-(id) init
{
if( (self=[super init] )) {
label = [CCBitmapFontAtlas bitmapFontAtlasWithString:#"ABC" fntFile:#"bitmapFontTest.fnt"];
[self addChild:label];
CGSize s = [[CCDirector sharedDirector] winSize];
label.position = ccp(s.width/2, s.height/2);
label.anchorPoint = ccp(0.5f, 0.5f);
label.visible = NO; //hide it first
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
return self;
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CCSprite AChar = (CCSprite) [label getChildByTag:0];
CCSprite BChar = (CCSprite) [label getChildByTag:1];
CCSprite CChar = (CCSprite) [label getChildByTag:2];
id fade_in = [CCFadeIn actionWithDuration:3];
label.visible = YES;
[AChar runAction:fade_in];
[BChar runAction:fade_in];
[CChar runAction:fade_in];
return YES;
}
The effect is the "ABC" will fade in once I touched the screen, then I tried to use the
CallFuncND to call the next string to fade in while the current string is displayed.
But this seems to make things very complex.
Is there a easier way to get this effect done?
Any suggestion will be appreciate.
I feel like you are going in the right direction with this one. You could have each letter be a separate sprite and store them in an array and then run them each one by one.
The call function can be started by:
[self displayNextSprite:spriteArray nextIndex:0];
And the function is:
// Warning, This assumes you are not passing it an empty array, you may want to put in a check for that
-(void)displayNextSprite:(NSMutableArray*)spriteArray nextIndex:(NSUInteger)nextIndex
{
CCSprite *nextSprite = [spriteArray objectAtIndex:nextIndex];
id action1 = [CCFadeIn actionWithDuration:3];
// or = [CCPropertyAction actionWithDuration:3 key:#"opacity" from:0 to:255];
// The last letter
if(nextIndex == ([spriteArray count] - 1))
{
[nextSprite runAction:action1];
}
else // continue to the next letter
{
id callFunc = [CCCallFunc actionWithTarget:self selector:#selector(displayNextSprite:spriteArray nextIndex:nextIndex+1)];
id sequence = [CCSequence actionOne:action1 two:callFunc];
[nextSprite runAction:sequence];
}
}