I have problem with following code.
mySpriteArray=[[NSMutableArray alloc] init];
star=[CCSprite spriteWithFile:#"22.png"];
for(int i=0;i<10; i++)
{
[mySpriteArray insertObject:star atIndex:i];
}
// NSLog(#"x=%i",[mySpriteArray count]);
for (int i=0; i<10; i++) // Opponents is NSMutableArray
{
CCSprite *tempSprite = (CCSprite *) [mySpriteArray objectAtIndex:i];
tempSprite.position=ccp(100,100);
[self addChild:tempSprite];
}
}
where star is a object of CCSprite and mySpriteArray is a mutable array.The problem is that when i run the program it crash and say
* Assertion failure in -[GameScene addChild:z:tag:], /Users/salimsazzad/Desktop/balon hunter/libs/cocos2d/CCNode.m:305
2010-10-08 19:05:35.854 balon hunter[3967:207] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'child already added. It can't be added again'.
i can't understand whats wrong,i am adding 10 object,not 1 object in 10 times because CCSprite *tempSprite = (CCSprite *) [mySpriteArray objectAtIndex:i];creating a new object each time.
so what is the problem???
You have created your star object once and added it 10 times to array:
star=[CCSprite spriteWithFile:#"22.png"];
for(int i=0;i<10; i++)
{
[mySpriteArray insertObject:star atIndex:i];
}
So your array contains the same object and that's the reason of assertion you get.
Related
probably this is a simple problem to solve, since I'm quite new.
I have a scene with a waiter holding a tray of food
(the food is a random CCSprite choosen from an array)
each time he comes onscreen he holds a new piece of food
(the user touches the food and the waiter walks off to return
again with a new piece of food
however I cant seem to delete the old peice of food from the screen
as it says the child is already added...
any help would be great
-(id) init
{
///other code then...
waiterOnSCreen
= [CCSprite spriteWithSpriteFrameName:#"CatUP.png"];
waiterOnSCreen.position = ccp(CatOffSCreenPosX, catXpos);
[self addChild:waiterOnSCreen z:0];
//moving the waiter
// the random food sprite is added later to the waiter
// [waiterOnSCreen addChild:myRandomSprite];
}
-(void)LoadRandomFood
{
///I make my array here then and add CCSprites
RandomFood = [[NSMutableArray alloc]initWithObjects:
//cake,
RandomMuffin,
RandomMeat,
RandomCake,//
nil];//
int i = 0;
int count= [RandomFood count];
if (i < count)
{
int i = arc4random() % [RandomFood count];
myRandomSprite = (CCSprite *)[RandomFood objectAtIndex:i];
//waiterOnSCreen is a CCSprite added on the init
[waiterOnSCreen addChild:myRandomSprite];
myRandomSprite.position=ccp(290,220);
myRandomSprite.tag = RandomFoodTag;
}
}
later
in if(CGRectContainsPoint(waiterOnSCreen.boundingBox, location))
{
//trying to remove the food here
//Ive already tried to remove the sprite using
[self removeChildByTag:RandomeObjectTag];
//and also
CCSprite *wantedSprite = (CCSprite *)[self getChildByTag:RandomFoodTag];
[wantedSprite removeFromParentAndCleanup:YES];
}
}
Im guessing its also crashing 'child already added. It can't be added again'
as its trying to add RandomMuffin,RandomMeat,RandomCake, again
im not too sure how to fix that.
You shouldn't need to create another array just to delete a sprite. If you can provide a more complete picture of your project I would be happy to review it. Can you post the entire code file where you are doing your work? Or multiple files? On inspecting some of your samples further a few concerns in LoadRandomFood you are allocating an NSMutableArray without checking if RandomFood already has a value.
Something like this would be safer though note I am just doing a sanity check here. It would be more efficient to just fill in RandomFood once on init or elsewhere that is not going to be executed constantly.
-(void)LoadRandomFood
{
if (RandomFood)
{
[RandomFood release];
}
///I make my array here then and add CCSprites
RandomFood = [[NSMutableArray alloc]initWithObjects:
//cake,
RandomMuffin,
RandomMeat,
RandomCake,//
nil];//
int i = 0;
int count= [RandomFood count];
if (i < count)
{
int i = arc4random() % [RandomFood count];
myRandomSprite = (CCSprite *)[RandomFood objectAtIndex:i];
//waiterOnSCreen is a CCSprite added on the init
[waiterOnSCreen addChild:myRandomSprite];
myRandomSprite.position=ccp(290,220);
myRandomSprite.tag = RandomFoodTag;
}
}
In the case of the crash when adding a sprite are you sure the removeChildByTag call is being executed? Is it possible you are re-assigning the tag to a new sprite before deleting the old one? Have you stepped through the logic in the debugger? Again it would help to see a more complete code file here.
I had to make another array to access the sprite and delete it
NSMutableArray *_buffer = [NSMutableArray new];
[_buffer insertObject:myRandomSprite atIndex:0];
CCSprite *sprite = (CCSprite *)[_buffer objectAtIndex:0];
[sprite removeFromParentAndCleanup:YES];
I have Two layers, the game and the hudlayer. Hud is on top of HelloWorldLayer.
I've got a button which I press to reload the hudlayer which is just numbers showing up on the screen to capture what's on gameplay.
buttonTapped: is declared on the HudLayer(which goes before HelloWorldLayer) implementation.
HudLayer and HelloWorldLayer are on the same file which is helloworldlayer.m
I press the button and:
- (void)buttonTapped:(id)sender
{
int number = 6;
//Heres the problem
//I dont know how to change this part...
[[HelloWorldLayer]->changedNumber = Number; ///How do I give changedNumber Number's value?????
_label.string = [NSString stringWithFormat:#"Number: %d",number];
}
HelloWorldLayer has a property:
#property (assign) int changedNumber;
Sounds like your sender is HelloWorldLayer.
You can try:
sender.changedNumber = Number;
Try this:
CCArray* layers = [[CCDirector sharedDirector] runningScene].children;
for(int i=0; i<[layers count]; i++)
{
CCNode *node = [layers objectAtIndex:i];
if ([node isKindOfClass:[HelloWorldLayer class])
{
HelloWorldLayer *obj = (HelloWorldLayer*)node;
obj.changedNumber = Number;
break;
}
}
I am populating an NSMutableArray with CCSprites and adding them to the current layer which is added to a scene. When i try to access the elements of the array I get a bad access error
sprites = [[[NSMutableArray alloc] init]autorelease];
int j = 0;
for (int i=0; i<[sprites count]; i++)
{
j=i+1;
sprite = [CCSprite spriteWithFile:[NSString stringWithFormat:#"intro%d.png",j]];
sprite.position = ccp(WIDTH/2, HEIGHT/2+(i*HEIGHT));
[sprites addObject:sprite];
}
for (int i = 0; i<[sprites count]; i++)
{
[self addChild:[sprites objectAtIndex:i]];
}
[self scheduleUpdate];
}
return self;
This is done in the init method and sprites and sprite are declared in the header file. Inside the update method, I have
sprite = [sprites objectAtIndex:1];
sprite.position = ccpAdd(sprite.position, ccp(0,dy));
CCSprite* spr = [sprites objectAtIndex:2];
spr.position = ccpAdd(spr.position, ccp(0,dy));
When control is passed to the update method, I get a bad access. I would be glad if anyone could help me
This is happening because you created an autorelease object, that you then try to access later in your update code. The sprites array has a retain count of zero (since it is autorelease), and gets deallocated, you then try to access it, and get a bad-access error.
To fix, you should not make the sprites array an autorelease object. Since you plan on using the object later on in your code, you should keep it retained (by not adding the autorelease). You should also then make sure to release the array in the (whatever the parent object that is operating on the array)'s dealloc method.
- ( void ) dealloc {
[ sprites release ];
[ super dealloc ];
}
You should retain the sprites array (make certain you release it in your object's dealloc). Since it is autoreleasable, during the cycle from your init to your update method, it gets released.
Also (strange) , if your code is as shown, sprites.count is 0 when you start your first loop. You wont get much in there :).
The others are right (the autorelease call is the culprit - you should just remove it and then release the array in the dealloc method of the scene).
Also note that [[[NSMutableArray alloc] init]autorelease] has the same effect as [NSMutableArray array] (for your scenario, though, this is not what you need).
I use CCSpriteBatchNode, but I got some exception error. Here is my code:
//Use CCSpriteBatchNode.....!!!
CCSpriteBatchNode *singleGameSpriteBatchNode;
//if < 16 , create dice
unsigned int createDiceNumber = 4 - (maxDiceNumber%4) ;
for (int i = 0; i<createDiceNumber; i++) {
CCLOG(#"Total dice number = %d" , createDiceNumber);
int randomDiceNumber = arc4random()%17;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"singleGameImage.plist"];
singleGameSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:#"singleGameImage.png"];
Dice *tempCreateDice = [[Dice alloc]initWithSpriteFrameName:[self getDicePicture:randomDiceNumber]];
[singleGameSpriteBatchNode addChild:tempCreateDice];
[self addChild:singleGameSpriteBatchNode z:5];
[createDiceArray addObject:tempCreateDice];
[tempCreateDice setPosition:ccp( 40 , 220-(60*i))];
[tempCreateDice setRecentPosition:ccp( 40 , 220-(60*i))];
[tempCreateDice setDiceType:randomDiceNumber];
[tempCreateDice release];
maxDiceNumber++;
mustMoveDiceNumber++;
}
CCLOG(#"length = %d" , [createDiceArray count]);
}
It succeeds to create dice with image. But when I use the ccTouchesBegan method I get some error message:
*** Assertion failure in -[Dice draw], /Users/xxxxx/Documents/C_Program/iOs Practice(2011)/DiceGame/DiceGame/libs/cocos2d/CCSprite.m:576
2012-08-13 02:32:47.921 DiceGame[11103:c07] *** Terminating app due to uncaught
exception 'NSInternalInconsistencyException', reason: 'If CCSprite is being
rendered by CCSpriteBatchNode, CCSprite#draw SHOULD NOT be called'
PS: Dice is a class inherits from CCSprite that I created.
If it can't use CCSpriteBatchNode, maybe I should go back to use CCSprite.
Have you read the error message?
'If CCSprite is being rendered by CCSpriteBatchNode,
CCSprite#draw SHOULD NOT be called'
Either don't override the -(void) draw method or don't use the sprite batch node.
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];