accessing instance variable in cocos2d scheduled method crashes - ios5

fresh to objC and cocos2d :)
i'm following "learn cocos2d game development with iOS5", in chapter4, there is a "DoodleDrop" game.
define some variable in GameScene.h like this
#interface GameScene : CCLayer
{
CCSprite *player;
CGPoint playerVelocity;
CCArray *spiders;
CGSize screenSize;
int dropedSpidersCount;
float duration;
}
+ (CCScene *)scene;
#end
in GameScene.m the init method looks like this
- (id)init
{
if (self = [super init]) {
duration = 4.0;
[self createPlayer];
[self createSpiders]; // spiders were inited here.
[self resetSpiders];
[self schedule:#selector(chooseSpider:) interval:0.7];
}
return self;
}
while in chooseSpider, i cannot access spiders, xcode broke
in other methods, spiders or duration just behave normally, why does this happens?
gist code added
https://gist.github.com/2940466

After inspecting your code, I suggest you to try this fix:
- (void)createSpiders
{
CCSprite *tempSpider = [CCSprite spriteWithFile:#"spider.png"];
CGSize spiderSize = [tempSpider texture].contentSize;
int spiderCount = screenSize.width / spiderSize.width;
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
for (int i = 0; i < spiderCount; i++) {
CCSprite *spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider];
[spiders addObject:spider];
}
}
where the only difference is in the line:
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
Indeed, if you do not retain you spiders object, it will be autoreleased at the next run loop iteration.
OLD ANSWER:
Without seeing more code it is not possible to say exactly what is happening, but it seems that in the interval between creating the spiders and the actual execution of chooseSpiders, your spiders array gets deallocated.
As a quick try, I would suggest adding:
[spiders retain];
before calling
[self schedule:#selector(chooseSpider:) interval:0.7];
and see wether the crash keeps happening.
if you provide more code, it could be possible to help you further.

Related

CCSprite doesn't go invisible -iphone

in .h file
CCSprite *backwheels;
in .m file
backwheels = [CCSprite spriteWithFile:#"wheels_back.png"];
backwheels.position = ccp(400,120);
[self addChild:backwheels];
-(void) showGameOver {
backwheels.visible = false;
}
but when Game is Over backWheels still appears on scene..? !
any help ?!
NOTE:i have synthesized backWheels too,but still doesn't work for me.
I can give you a quick fix. This is not the best approach, the best one is to find out what exactly causes such a behavior, but i can't do that without seeing the rest of your code. Anyway this is how you can access backwheels sprite in showGameOver method. When you create the sprite make it this way:
backwheels = [CCSprite spriteWithFile:#"wheels_back.png"];
backwheels.position = ccp(400,120);
backwheels.tag = 100; // whatever integer value you wish
[self addChild:backwheels];
Then you retrieve it in showGameOver :
backwheels = [self getChildByTag:100];
backwheels.visible = false;
I believe it's gonna work.
You could always just change the opacity of the sprite i.e.
-(void) showGameOver {
backwheels.opacity = 0.0f;
}
And then when you want it to reappear change it to
backwheels.opacity = 1.0f;
Use Remove Child :
[self removeChild:backwheels cleanup:YES];

Cocos2d NSMutable Array mutated while enumerated

I've been working on a game, and at a point I didn't have issues removing the enemy game objects (right now they are subclassed from CCSprite and I know that's not the best way)
But I'm not sure what I changed to make it crash when the program attempts to removeChild from _targets after they have been added to targetsToDelete.
I tried moving things around, I just don't know how I am adding or editing the array while its being created... Any help or advice would be great!
And actually if you had any pointers on how best to create game enemies, do you subclass NSObject or CCNode? I heard to divide them into component classes but I had no clue what they meant.
//Projectile Target collision
-(void)update:(ccTime)dt {
for (spygot *target in _targets) {
CGRect targetRect = CGRectMake(
target.position.x - (target.contentSize.width/2),
target.position.y - (target.contentSize.height/2),
target.contentSize.width,
target.contentSize.height);
//Collision Detection Player
CGRect playerRect2 = CGRectMake(
_controlledSprite.position.x - (_controlledSprite.contentSize.width/2),
_controlledSprite.position.y - (_controlledSprite.contentSize.height/2),
_controlledSprite.contentSize.width,
_controlledSprite.contentSize.height);
NSMutableArray *projectilesToDelete = [[NSMutableArray alloc] init];
for (Projectile *projectile in _projectiles)
{
NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
CGRect projectileRect = CGRectMake(
projectile.position.x - (projectile.contentSize.width/2),
projectile.position.y - (projectile.contentSize.height/2),
projectile.contentSize.width,
projectile.contentSize.height);
BOOL monsterHit = FALSE;
if (CGRectIntersectsRect(projectileRect, targetRect))
{
NSLog(#"hit");
target.mhp = target.mhp - 1;
monsterHit = TRUE;
if (target.mhp <= 0)
{
[targetsToDelete addObject:target];
}
}
for (spygot *target in targetsToDelete)
{
[self removeChild:target cleanup:YES];
[_targets removeObject:target];
}
if (monsterHit)
{
[projectilesToDelete addObject:projectile];
}
[targetsToDelete release];
}
for (Projectile *projectile in projectilesToDelete)
{
[_projectiles removeObject:projectile];
[self removeChild:projectile cleanup:YES];
}
[projectilesToDelete release];
}
It looks like the code that you've pasted is all from within a for loop iterating over _targets. How does the variable target get initialized?
Usually when I get this sort of error it's because I have the code in a block or am in some other way on a nebulous thread. How sure are you that this bit of code is not running more than once at the same time?
You could try wrapping it in the following:
dispatch_async(dispatch_get_main_queue(), ^{
// do everything here.
});
As for advice about using CCSprite for your game enemy objects, my advice is fix it when it becomes a problem. Are you seeing issues with it right now? Premature optimization is almost as bad as doing it wrong in the first place. You'll know better at the end of the project how you should have done it earlier. ;)
I guess you know that you cannot remove elements from the array while you iterate over it. That is why you have targetsToDelete array.
But it looks to me that you do remove targets to soon.
Try this:
finish iterating the main loop and finish collecting targets to the targetsToDelete array and only after main loop is done remove the targets.

NSMutableArray and batchNode problems

I'm making a little game, here is some example code of whats going on:
-(id) init
{
self.arrowProjectileArray = [[[NSMutableArray alloc] init] autorelease];
self.batchNode = [CCSpriteBatchNode batchNodeWithTexture:[[CCTextureCache sharedTextureCache] addImage:#"arrow.png"]];
[self addChild:_batchNode z:2];
for (CCSprite *projectile in _arrowProjectileArray) {
[_batchNode removeChild:projectile cleanup:YES];
}
[_arrowProjectileArray removeAllObjects];
self.nextProjectile = nil;
}
}
-(void) callEveryFrame:(ccTime)dt{
for (int i = 0; i < [_arrowProjectileArray count];i++) {
CCSprite *cursprite = [_arrowProjectileArray objectAtIndex:i];
if (cursprite.tag == 1) {
float x = theSpot.x+10;
float y = theSpot.y+10;
cursprite.position = ccp(x, y);
}
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[_batchNode addChild:_nextProjectile z:1 tag:1];
[_arrowProjectileArray addObject: _nextProjectile];
[self spriteMoveFinished];
}
-(void) dealloc
{
self.arrowProjectileArray = nil;
self.nextProjectile = nil;
[super dealloc];
}
The only code that I included was code that is relevant to the arrow's projection.
The arrow shoots fine, the problem is every time I shoot the stupid thing, I think it shoots a new arrow, but puts multiple arrows onto of that 1 arrow and makes it look like a fat ugly arrow pixel thing. What am I doing wrong? I'm not too familiar with NSMutableArray, but I'm currently stuck.
In init method, you create a new NSMutableArray instance and assign it to self.arrowProjectileArray, then you traverse the arrowProjectileArray in the following lines using a for loop. If addChild: method does not add anything to arrowProjectileArray, then your code has a logic mistake, because what you do by traversing arrowProjectileArray is traversing an empty array, which means you do nothing in that code.
You should double-check what you intend to do and what your code is doing actually.
I solved my own problem by doing a little bit of research, I also got rid of the batch node.

iphone cocos2d CCSprite EXC_BAD_ACCESS

Friends i am new to cocos2d programming and Mac in general!
I have noticed this EXC_BAD_ACCESS errors ruining most of my time... Take the following snippet of code from the Geek & Dad's tutorial...
-(void) AddEnemyAtX:(int)x Y:(int)y {
CCSprite *enemy1 = [CCSprite spriteWithFile:#"enemy1.png"];
enemy1.position = ccp(x,y);
[self addChild:enemy1];
[self animateEnemy:enemy1];
NSLog(#"%#", enemy1);
}
-(void) animateEnemy:(CCSprite *)enemy {
ccTime actualDuration = .5;
id actionMove = [CCMoveBy actionWithDuration:actualDuration
position:ccpMult(ccpNormalize(ccpSub(_player.position,enemy.position)), 10)];
id actionFinished = [CCCallFunc actionWithTarget:self
selector:#selector(animateEnemyFinished:)];
[enemy runAction:[CCSequence actions:actionMove,actionFinished,nil]];
}
-(void) animateEnemyFinished:(id)sender {
CCSprite *enemy = (CCSprite *)sender;
[self animateEnemy:enemy];
}
here _player is a global variable and accessible everywhere, I call AddEnemyAtX: Y: and pass some coordinates. My problem is the first time the loop runs fine... But again when the control is passed from animateEnemyFinished to animateEnemy then the app crashes mentioning "EXC_BAD_ACCESS"!
From what i figured out, the Sprite reference is not passed correctly! Help!
CCSprite *enemy1 = [CCSprite spriteWithFile:#"enemy1.png"];
gives you an autoreleased object. This means you should not call
[enemy1 release]
and 2nd after you set
enemy1=nil
you can't do
[self animateEnemy:enemy1];
because you give nil to animateEnemy:
Removing
[enemy1 release];
and
enemy1 = nil;
from your code should fix your problem.
Wow!!
Atlast figured it out...
A small mistake... just replacing the line
id actionFinished = [CCCallFunc actionWithTarget:self
selector:#selector(animateEnemyFinished:)];
with
id actionFinished = [CCCallFuncN actionWithTarget:self
selector:#selector(animateEnemyFinished:)];
did the trick! What i understood from this was that #selector() passes the id of the object which called upon it but at the same time when we use it along with CCCallFuncN it passes the id of the Node just parent to the object which called upon it!

Few questions on iphone memory management and cocos2d

I'm working on my first app and have a few questions on memory management.
First Question:
I'm working on an intro scene that looks like this
#import "Intro_Scene.h"
#import "Main_Menu.h"
#import "Label.h"
#implementation Intro_Scene
#synthesize logo,label;
-(id) init
{
self = [super init];
if(self != nil)
{
//Load logo image and set position
logo = [Sprite spriteWithFile:#"AVlogo_1.png"];
logo.position = ccp(-50, 0);
logo.scale = 1.8f;
[self addChild: logo];
//Creates 3 actions for the logo sprite
id action0 = [MoveTo actionWithDuration:0 position:ccp(160,270)];
id action1 = [FadeIn actionWithDuration:3];
id action2 = [FadeOut actionWithDuration:3];
//Logo runs the actions
[logo runAction: [Sequence actions:action0,action1, action2, nil]];
//Schedules the changeScene method to switch scenes to main menu within 6 seconds of loading.
[self schedule: #selector(changeScene) interval:6.0f];
//Creates a label and positions it, Alternative Visuals
label = [Label labelWithString:#"Alternative Visuals" fontName:#"Verdana" fontSize:22];
label.position = ccp(160, 120);
[self addChild:label];
}
return self;
}
//Method called after intro has run its actions, after 6 seconds it switches scenes.
-(void)changeScene
{
[self removeChild:logo cleanup:YES];
[self removeChild:label cleanup:YES];
Main_Menu *mainMenu = [Main_Menu node];
[[Director sharedDirector] replaceScene: mainMenu];
}
-(void)dealloc
{
[[TextureMgr sharedTextureMgr] removeUnusedTextures];
[label release];
[logo release];
[super dealloc];
}
#end
Have I released everything correctly, and avoided leaks? I ran it in instruments multiple times and it found no leaks and used about 2mb of memory, is that to much or the amount to be expected? Also does the dealloc method get called when the scene is replaced?
Question 2:
My main menu is set up like this
#import "Main_Menu.h"
#import "Sprite.h"
#import "cocos2d.h"
#implementation Main_Menu
#synthesize background, controlLayer;
-(id) init
{
self = [super init];
if(self != nil)
{
//Create the default background for main menu not including directional pad and highlight box
background = [Sprite spriteWithFile:#"Main_Menu_bg.png"];
background.position = ccp(160,240);
[self addChild:background];
//Adds the control later class to the main menu, control layer class displays and controls the directional pad and selector.
ControlLayer *layer = [[ControlLayer alloc] init];
self.controlLayer = layer;
[layer release];
[self addChild: controlLayer];
}
return self;
}
-(void) dealloc
{
[seld removeChild:background cleanup:YES];
[[TextureMgr sharedTextureMgr] removeUnusedTextures];
[background release];
[controlLayer release];
[super dealloc];
}
#end
Once again am I doing everything correctly? The layer ControlLayer I'm adding to this scene contains a directional pad sprite that the user uses to navigate the menu. In instruments It also confirms that their is no memory leaks, and it uses 4.79 mb of memory. Once again is that a reasonable amount? I will most likely switch to using AtlasSprite and AtlastSpriteManager to conserve memory.
I'm new to cocos2d, so if you see I'm doing anything wrong point it out! I'd rather fix bad habits in the early stages. And if you have any future tips for memory management please share.
Don't release logo, label, or background. You didn't alloc/copy/new/retain them, so you don't own them and must not release them.
I assume the controllerLayer property has the retain attribute? If not, you probably mean to do so.
In general I'd suggest two things going forward.
Read and understand the Cocoa Memory Management Fundamentals
Run the Clang analyzer on your code. This is available in Xcode 3.2 via Build->Build and Analyze. It will help detect these memory issues.
Also check out this SO question.