iphone cocos2d CCSprite EXC_BAD_ACCESS - iphone

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!

Related

#selector multiple Arguments error with CCCallFunc

I am trying this stuff but there is Run Time Error in the next method i.e. changeCardsfirst2 I am unable to find what goes wrong. Please suggest me for this error
-(void) changeFirst2Cards : (CCSprite *) chgcard0 :(CCSprite *) chgcard1
{
id a1 = [CCDelayTime actionWithDuration:0.5f];
id a2 = [CCCallFunc actionWithTarget:self selector:#selector(changeCardsfirst2:data:) ];
id action = [CCSequence actions:a1,a2,nil];
[self runAction:action];
}
-(void) changeCardsfirst2 : (id) sender data:(CCSprite *)chgcard1
{
[chgcard1 runAction:[CCMoveTo actionWithDuration:0.5f position:ccp(145,wSize.height-110)]]; // Error for EXEC_BAD EXCCESS
}
Thanks for the Advice in Advance
Your selector does not use the parameters required by CCCallFunc - it should have no parameters. CCCallFuncN will run a selector with the node as its first argument, and CCCallFuncND will run a selector with the node and an arbitrary void* pointer as its second argument.
You should use CCCallFuncN:
id a2 = [CCCallFuncN actionWithTarget:self
selector:#selector(changeCardsfirst2:)];
And change the selector accordingly to work with the node:
-(void) changeCardsfirst2:(CCNode*)chgcard1
{
id move = [CCMoveTo actionWithDuration:0.5f
position:ccp(145,wSize.height-110)];
[chgcard1 runAction:move];
}
The only possible reason is that chgcard1 was over-released somewhere earlier.
I've never used Cocos, but if you don't have the source code and using a static lib then just create the category of CCSprite and override the dealloc method (this is temporary just to see when it is called for this object). If you're building using Cocos sources then just create a breakpoint there.

Starting CCAction at particular time

I want to start InFinite CCAction at particular time. I tried using CCSequence but it supports only finite time animation.
Any idea?
Best Regards,
Paras
Put the action that you want to repeat inside of a method. Then put this in your init method
[[CCScheduler sharedScheduler] scheduleSelector:#selector(myMethod) forTarget:self interval:10 paused:NO];
This will call myMethod after 10 seconds, however once inside myMethod you'll want to unschedule it. So my method should look something like this.
- (void) myMethod
{
[[CCScheduler sharedScheduler] unscheduleSelector:#selector(myMethod) forTarget:self];
CCMoveBy *move = [CCMoveBy actionWithDuration:3 position:ccp(75,0)];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:move];
[mySprite runAction:repeat];
}
The last two lines are what you need.
CCMoveBy* move = [CCMoveBy actionWithDuration:3 position:ccp(75,0)];
CCCallFuncO* shot = [CCCallFuncO actionWithTarget:self selector:#selector(shoot:) object:enemy];
CCSequence* sequ = [CCSequence actions:move,shot,nil];
CCRepeatForever* repeat = [CCRepeatForever actionWithAction:sequ];
[sprite runAction:repeat]; //sprite here

Is my class going to lose its memory

I have a class with two methods listed below. I call them from another class in a function. In this function I new up my class then call fireTorpedoContinued. In C# everything would be great, but I started to think about it. I'm not retaining the class anywhere and there seems to be nothing to stop it from going out of memory between fire torpedo and firetorpedo continued. Does anything hold onto this class or do I need to retain it? For example does the execute function class retain the class? What if that was not there would I be in trouble?
- (void) fireTorpedoContinued
{
[self.torpedoData.explosionSprite stopAllActions];
CCPlace *placeAction = [CCPlace actionWithPosition:_endPoint];
CCShow *showAction = [CCShow action];
CCCallFunc *callFunctionDeathCheck = [CCCallFunc actionWithTarget:self.deathCheckSelectorTarget selector:self.deathCheckSelector];
CCFadeOut *fadeOutAction = [CCFadeOut actionWithDuration:1.0f];
CCCallFunc *callfunctionAction = [CCCallFunc actionWithTarget:self.completedSelectorTarget selector:self.completedSelector];
CCSequence *sequenceAction = [CCSequence actions:placeAction, showAction, callFunctionDeathCheck, fadeOutAction, callfunctionAction, nil];
[self.torpedoData.explosionSprite runAction:sequenceAction];
}
- (void) fireTorpedo
{
[self.torpedoData.torpedoSprite stopAllActions];
CCPlace *placeAction = [CCPlace actionWithPosition:_startPoint];
CCShow *showAction = [CCShow action];
CCMoveTo *moving = [CCMoveTo actionWithDuration:2.0f position:_endPoint];
CCHide *hideAction = [CCHide action];
CCAction *callCompletedFunction = [CCCallFunc actionWithTarget:self selector:#selector(fireTorpedoContinued)];
CCSequence *sequenceAction = [CCSequence actions:placeAction, showAction, moving, hideAction, callCompletedFunction, nil];
[self.torpedoData.torpedoSprite runAction: sequenceAction];
}
Your question is a little fuzzy, but everything you need to know is explained here:
http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmObjectOwnership.html#//apple_ref/doc/uid/20000043-BEHDEDDB

How to do this in Cocos2d?

Sorry about the poor question title, it's just that this seems to big for a title. So here's the dirt:
I am making a game (obviously) and I want the enemies to shoot (not necessarily at the player). I want the shoot method to be in the Enemies file, so as not to clutter up my HelloWorldLayer.m file even more. Here's what I'm using right now:
HelloWorldLayer.m
-(void)addEnemy:(BigAndStrongEnemy *)enemy {
enemy = nil;
if((arc4random() % 4) == 3) {
enemy = [BigAndStrongEnemy enemy];
} else {
enemy = [SmallAndFastEnemy enemy];
}
if(buffDude.position.y > character.position.y || buffDude.position.y < (character.position.y + 10)) {
}
int rand = arc4random() % 320;
if((arc4random() % 2 == 1)) {
[enemy setPosition:ccp(0,rand)];
}else{
[enemy setPosition:ccp(480,rand)];
}
[self animateEnemy:enemy];
[self addChild:enemy];
}
-(void)animateEnemy:(BigAndStrongEnemy *)enemy2 {
float randX = arc4random() % 480;
float randY = arc4random() % 320;
int rand = arc4random() % 320;
CGPoint moveToPoint = CGPointMake(randX, (randY - rand));
[enemies addObject:enemy2];
action = [CCSequence actions:
[CCMoveBy actionWithDuration:1 position:ccpMult(ccpNormalize(ccpSub(moveToPoint, enemy2.position)), 75)],
[CCMoveBy actionWithDuration:3 position:ccp(buffDude.position.x,buffDude.position.y)],
nil];
CCCallFuncO *a = [CCCallFuncO actionWithTarget:self selector:(#selector(shoot:)) object:enemy2];
CCSequence *s = [CCSequence actions:action,a, nil];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:s];
[enemy2 runAction:repeat];
}
And here's the Shoot info from the Enemies class:
Enemies.m:
-(void)shoot:(id)sender {
BigAndStrongEnemy *enemy = (BigAndStrongEnemy *)sender;
[enemy shoot];
}
-(void)spriteMoveFinished:(id)sender {
CCSprite *b = (CCSprite *)sender;
[self removeChild:b cleanup:YES];
}
-(void)shoot {
buffDude = [CCSprite spriteWithFile:#"bigAndStrongEnemy.gif"];
CCSprite *b = [CCSprite spriteWithFile:#"bullet.gif"];
b.position = ccp(self.position.x,self.position.y);
b.tag = 2;
[self addChild:b];
[bullets addObject:b];
CGSize winSize = [[CCDirector sharedDirector] winSize];
CGPoint point = CGPointMake((winSize.width - (winSize.width - self.position.x)),0);
[b runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:0.5 position:point],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
Every time the 3 seconds goes by, the app crashes, and goes to the breakpoint in the CCCallFuncO file. I haven't touched it, is the thing. I am completely confused. Any help is greatly appreciated. Sorry about the long question. Thanks!!
It looks like your CCCallFuncO is calling [self shoot:enemy2] where self is the HelloWorldLayer object, but shoot is defined in the Enemies class.
Edit:
buffDude should be released before assigning it and retain it afterwards, like this:
Change this:
buffDude = [CCSprite spriteWithFile:#"bigAndStrongEnemy.gif"];
to this:
[buffDude release];
buffDude = [CCSprite spriteWithFile:#"bigAndStrongEnemy.gif"];
[buffDude retain];
This assumes that this is the only place where buffDude is assigned. If it is also assigned elsewhere, make sure you properly retain and release it there as well.
Although, the more I think about it, I don't understand why you create a new buffDude every time shoot is called. I'm not sure what you're doing, so I won't say it's wrong, but it doesn't look right to me anyway.
from what i see (and the fact that i only worked with cocos2d-x) i can only tell you to test whether it steps inside your shoot funciton or not, but i guess you are somhow using CCCallFuncO bad, maybe you have to call it passing enemy2 also for the actionWithTarget parameter (i can tell that becouse that's how cocos2d-x works), also check if big and small enemy types can be converted together freely.

What is the best way to start Cocos2d Animation of Multiple Sprites?

I have an array randomAlphabets which contains CCSprite objects. I need to start animation on these objects. The randomAlphabets array (NSMutable) can contain max of 4 elements. I am running a loop and then starting the animation. Is this the correct way?
-(void) startAnimation:(CCSprite *) sprite
{
[self generateRandomCoordinates];
id actionMove = [CCMoveTo actionWithDuration:3.0 position:ccp(x,y)];
id actionRotate = [CCRotateBy actionWithDuration:0.0 angle:rotateBy];
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:#selector(finishedMoving:)];
[sprite runAction:[CCSequence actions:actionMove,actionRotate, actionMoveDone, nil]];
}
-(void) addAlphabetsOnScreen
{
for (int i=0; i<=randomAlphabets.count -1; i++) {
CCSprite *sprite = [randomAlphabets objectAtIndex:i];
[self generateRandomCoordinates];
sprite.position = ccp(x,y);
[self addChild:sprite];
[self startAnimation:sprite];
}
}
Sure, why not?
If have performance issues or sprites not starting their anims simultaneously, you might want to "prepare" the sequences for each sprite in one step (maybe after loading the level) and then just kick them all of in another step. 4 Sprites starting at the same time seems not too tough though.