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).
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];
hi there I am making a game with Sprite Kit and I have a method which has a NSArray, a for loop and some SKACTIONS. Now, this array has 7 items. the for loop randomly picks up one of the array's content and shows on the screen and using SKActions, that item scales down and another scales up. however, at the bottom of the similuator, it shows that numbers of nodes keep piling up. for example, if one sprite appears, instead of 1 node, 4 nodes shows as the number of nodes at the bottom of Simulator's screen. What do you think might be wrong here?
here is my code.
-(void) ItemMakerMe {
myArray = [[NSMutableArray alloc] initWithObjects:#"item1", #"item2", #"item3", #"item4", #"item5", #"item6", #"MyNull", nil];
NSUInteger arraypicker = arc4random() % [myArray count];
for (int i=0; i < arraypicker; ++i) {
NSString *d1 = [myArray objectAtIndex:i];
sprites = [SKSpriteNode spriteNodeWithImageNamed:d1];
sprites.name = #"items";
sprites.position = CGPointMake(ScalarRandomRange(0, self.size.width/2), ScalarRandomRange(0, self.size.height/2));
sprites.xScale = 0;
sprites.yScale = 0;
[self addChild:sprites];
}
}
-(void) OneItemONLY {
SKAction *appear = [SKAction scaleTo:0.19 duration:0.5];
SKAction *waiter = [SKAction waitForDuration:0.5];
SKAction *scaleDown = [SKAction scaleTo:0.0 duration:0.5];
SKAction *remover = [SKAction removeFromParent];
[sprites runAction:[SKAction sequence:#[appear, waiter, scaleDown, remover]]];
}
Also in the init method I run it like this:
[self runAction:[SKAction repeatActionForever:[SKAction sequence:#[[SKAction performSelector:#selector(ItemMakerMe) onTarget:self],
[SKAction waitForDuration:2.0]]]]];
[self runAction:[SKAction repeatActionForever:[SKAction sequence:#[[SKAction performSelector:#selector(OneItemONLY) onTarget:self],
[SKAction waitForDuration:2.0]]]]];
if I understood correctly, I believe that the for loop runs the action of the last node of i value but still adds the other sprites before the loop ends.
I.E: Lets say the arraypicker has a value of 4, then the for-loop will then select the NSString from the array, initialize the sprites object with the image, name, position, etc and then it is added to self's children, this repeats 4 times but only the last added instance of sprites will have the image you want and will run the actions.
What I mean is that if you only want to select a random value from the array, and set its properties maybe you could remove the for-loop.
Summary: [self addChild:sprites] is adding the same object i times and is causing an increment of nodes.
I'm currently starting with cocos2d-x to build games for blackberry/android/iOS.
I have the png and plist for the animations of a character created with texturepacker. I load them with CCSpriteBatchNode and CCSpriteFrameCache then I use a function created by me that loads all frames into an array of frames, then create a CCAnimation object and store the CCAnimate object created with the animation (code is more clear) the thing is that I have a function that detect touches and it is supposed to cycle through all animations, but it always crashes.
here is some code (this goes in the init()):
_batchNode = CCSpriteBatchNode::create("Character/girl.png");
_cache = CCSpriteFrameCache::sharedSpriteFrameCache();
_cache->addSpriteFramesWithFile("Personajes/girl.plist");
_character = CCSprite::createWithSpriteFrameName("girlneutral1.png");
_character->setPosition(ccp(winSize.width * 0.1, winSize.height * 0.5));
_batchNode->addChild(_character, 1);
this->addChild(_batchNode);
createAnimation(0, "girlpush", 8, 0.15f);
createAnimation(1, "girlneutral", 4, 0.3f);
createAnimation(2, "girlcrash", 12, 0.3f);
createAnimation(3, "girljump", 12, 0.2f);
createAnimation(4, "girltrick", 12, 0.3f);
_character->runAction(CCRepeatForever::create( _charanimation[3]));
this->setTouchEnabled(true);
the function that loads the animations (_charanimation[] is just an array of CCAnimate):
void HelloWorld::createAnimation(int a, CCString animation_name, int frames, float delay)
{
CCArray* animframes = CCArray::createWithCapacity(frames);
char str[100] = {0};
for(int i = 1; i <= frames; i++)
{
sprintf(str, "%s%d.png", animation_name.getCString(), i);
CCSpriteFrame* frame = _cache->spriteFrameByName( str );
animframes->addObject(frame);
}
_charanimation[a] = CCAnimate::create(CCAnimation::createWithSpriteFrames(animframes, delay));
//_charanimation[a]->getAnimation()->setLoops(-1);
}
and I get the animation to work (the one I set with runAction()) but if I try to change the animation, for example, when I touch the screen:
void HelloWorld::ccTouchesBegan(cocos2d::CCSet* touches, cocos2d::CCEvent* event)
{
action++;
action%=5;
_character->stopAllActions();
_character->runAction( CCRepeatForever::create(_charanimation[action]));
char str[100];
sprintf(str, "Animation: %d", action);
pLabel->setString(str);
}
it crashes... I don't know if I am doing it wrong, if anyone could help, I will appreciate.
If I change the animation in runAction() it shows the animation correctly, but I can't change ingame with touches.
by the way, this is the error I get in console:
cocos2d-x debug info Assert failed: reference count should greater than 0
In function retain -- ..\..\cocoa\CCObject.cpp:92 m_uReference > 0 -- assertion failed
Its because the CCAnimate object you have created is autorelease object and you are not retaining the object. Autorelease objects will be deleted automatically if they are not retained, either explicitly or by some other object.
While adding to array You can do
CCAnimate *animate = CCAnimate::create(CCAnimation::createWithSpriteFrames(animframes, delay));
animate->retain();
_charanimation[a] = animate;
do not forget to release all the objects in the array when everything is over
_charanimation[index]->release();
Note:
Instead of using the simple C or C++ array you can use Cocos2d's CCArray which retains the object once it is added to the array.
For Example: (The memory handling is similar to Objective-C)
_charanimation = new CCArray();
//To add object to array
_charanimation->addObject(object); //This will retain the object
//To get an object
_charanimation->objectAtIndex(index);
_charanimation->lastObject();
_charanimation->randomObject();
//To remove object
_charanimation->removeObjectAtIndex(index); //This will release object
_charanimation->removeObject(object); //This will release object
//Dont forget delete array later
delete (_charanimation);
You can refer this link for more on CCArray
I am having an issue in Kobold2d-Cocos2d regarding the layering of CCSprite objects.
All of my code works fine, except that a certain tile (a CCSprite), held in an array, is supposed to always be on top. The code has an array of references to CCSprites (Tile) and goes along all the tiles swapping them with each other. So element 0 and 1 switch (in the array and on screen using a moveTo), then the new element 1 and 2 switch, then the new element 2 and 3 switch etc...
The idea is the Tile at the start of the chain leapfrogs to the front of the line!
I always want that first tile to stay on top, but it isn't. It only does sometimes (when it's above the other Tile it's switching places with on the screen, implying it was added later)
Here's the CCScene code in question:
//reference first CCSprite in array -works
Tile *firstTile = [tileArray objectAtIndex:(int)[[[selTracker oldSelectionArray] objectAtIndex:0]element]];
//How much time should be spend leapfrogging each two tiles broken up over 1 second.
float intervalTime = .75/(float)[[selTracker oldSelectionArray] count];
//Will use this to count each tile set we leapfrog
float currentPass = 0;
//TODO: destroy this wrapper class that holds number, use NSNumber. Fine for now.
SimpCoord *lastCord;
//Make moving sprite higher than all others. DOESN'T WORK?
[self reorderChild:firstTile z:100];
//Loop through all the tiles that were selected
for (SimpCoord *coord in [selTracker oldSelectionArray])
{
if (lastCord)
{
//Queue each tile to leapfrog with our moving tile
if ([self respondsToSelector:#selector(switchTilesFuncWrapper:)])
{
NSArray *argArray = [NSArray arrayWithObjects:
[NSNumber numberWithInt:[lastCord element]],
[NSNumber numberWithInt:[coord element]],
[NSNumber numberWithFloat:(intervalTime)], nil];
[self performSelector:#selector(switchTilesFuncWrapper:) withObject:argArray afterDelay:((currentPass) * intervalTime)];
}
currentPass++;
}
lastCord = coord;
}
`
That calls the following code by the way that actually swaps the two Tiles (I excluded the function wrapper middleman I needed for multiple arguments):
(In case it makes it easier to understand, rather than use a 2d array to hold all my tiles I just only draw 10 per line, hence the tileCoordsByElement method - but that shouldn't be important)
//
// Switch two tiles on the screen and in the holder array
// onlySwitchArray pass in true to only swap tiles in organization array
// but provide no animation.
- (void) switchTiles:(NSNumber*)elePosVal secPos:(NSNumber*)elePos2Val timeToSwitch:(NSNumber*)time
{
float switchTime = [time floatValue];
int elePos = [elePosVal intValue];
int elePos2 = [elePos2Val intValue];
Tile *tmpTile = [tileArray objectAtIndex:elePos];
Tile *tmpTile2 = [tileArray objectAtIndex:elePos2];
//Move actual tiles on screen
//Move Tile is elePos (1) to elePos2
int curCol = 0;
int curRow = 0;
[self tileCoordsByElement:elePos2 x:&curRow y:&curCol];
CCSequence *tmpTile1_Seq = [CCSequence actions:
[CCMoveTo actionWithDuration:switchTime position:ccp((curRow-1)*tmpTile.width + tmpTile.width/2,
(curCol-1)*tmpTile.height + tmpTile.height/2)], nil];
[tmpTile runAction:[CCRepeat actionWithAction:tmpTile1_Seq times:1]];
//Move Tile that is elePos2 to elePos(1)
[self tileCoordsByElement:elePos x:&curRow y:&curCol];
CCSequence *tmpTile1_Seq2 = [CCSequence actions:
[CCMoveTo actionWithDuration:switchTime position:ccp((curRow-1)*tmpTile.width + tmpTile.width/2,
(curCol-1)*tmpTile.height + tmpTile.height/2)], nil];
[tmpTile2 runAction:[CCRepeat actionWithAction:tmpTile1_Seq2 times:1]];
//Swap tiles in array second
[tileArray replaceObjectAtIndex:elePos withObject:tmpTile2];
[tileArray replaceObjectAtIndex:elePos2 withObject:tmpTile];
}
Alright.
I'm aware some things I'm doing aren't exactly efficient and if it's not relevant I don't really want to focus to heavily on them. This is just a learning exercise for me - I just honestly don't understand why the origional Tile won't stay on top.
I've tried every resource or example I possibly can find for answers (sample included code, tutorials online, terrible book I bought, random somewhat related stackoverflow articles) and I'm getting nothing :\
In case it matters this is how I origionally added my sprites to the scene:
//Set up A CCSpriteBatchNode to load in pList/pngs of images
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"zAtl_BasicImages_64.plist"];
nodeBasicImages = [CCSpriteBatchNode batchNodeWithFile:#"zAtl_BasicImages_64.png" capacity:100];
//Initialize array used to track tiles in scene
tileArray = [[NSMutableArray alloc] init];
for (int i = 0; i<100; i++)
{
Tile *tmpTile = [[Tile alloc] init];
[tileArray addObject:tmpTile];
[self reorderChild:tmpTile z:-1];
}
Note: I checked and the zOrder on both Tiles is correct on the line before the MoveTo animation begins.
I added my sprites not to my layer, but to an image node because I was using a Sprite Atlas. So I was trying to access sprites that weren't even there. - I'm a new user so going to have to wait 8 hours until I can close. Peace. Why it didn't throw an error I'll never know.
I am trying to build up my first game with cocos2d. I am trying to insert bullets. when I get this error. The problem is it only occurs when a player fires not for enemy sprites. When this error occurs no only the position of player swaps with another and back also the bullet is destroyed after hitting two targets.
OpenGL error 0x0503 in -[EAGLView swapBuffers]
my weapon class has the following bullet implementation
if([self.bulletsArray count] <= ([self.numberOfBulletsPerFire intValue]*[self.numberOfBulletsOnScreen intValue]))
{
for (int i =0; i< [self.numberOfBulletsPerFire intValue]; i++) {
BulletClass *bullet = [[Bullet alloc]initWithPosition:position Direction:direction strength:self.weaponLevel spriteArray:spriteArray enemyArray:enemyArray base:base];
[self.bulletsArray addObject:bullet];
[self addChild:bullet];
[bullet release];bullet = nil;
}
}
in BulletClass i have the init method as :
(id)initWithPosition:(CGPoint)position
Direction:(KDirectionInput)direction
strength:(NSNumber *)strength
spriteArray:(NSMutableArray *)sprites
enemyArray:(NSMutableArray *)enemyArray
base:(CCSprite *)base{
if ((self = [super init])) {
self.base = base;
self.strength = strength;
self.movementDirection = direction;
self.spriteArray = sprites;
self.enemyArray = enemyArray;
self.velocity = 200/1;
self.bullet.position = position;
[self addChild:self.bullet z:2];
}
return self;
}
can anyone help me out here..
There are a few issues here that may be contributing to your problems.
First, while not a bug but more of a performance thing, you should not place counts and other object methods like intValue in your conditions, as this slows down the program. Define a local variable just before your loops that equals this number, and then use that variable in the loop so the software doesn't have count the same array or extract the same value over and over again every time it loops.
More importantly, I do not see anywhere that you are using a sprite frame cache (sprite batch), and if you are using sprite arrays that are very large where your array counts are high and manipulating and drawing these sprites many times as is typically requested by bullet operations, you could be adding a lot of burden to OpenGL unnecessarily, perhaps creating the buffer error you saw.
You should use a cocos sprite batch for your sprite arrays and give this another try; at the very least it will vastly improve the program's performance for bullet operations.