#selector multiple Arguments error with CCCallFunc - iphone

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.

Related

Can I pass a function name as an argument ?

I want to make this class dynamically perform functions on itself by passing different names to it. Is that possible ? Or rather: How is it possible ?
-(id)initWithMethod:(NSString*)method{
if ((self = [super init])){
[self method];
}
return self;
}
-(void) lowHealth {
CCSprite *blackScreen = [CCSprite spriteWithFile:#"blackscreen.png"];
blackScreen.anchorPoint = ccp(0,0);
[self addChild:blackScreen];
id fadeIn = [CCFadeIn actionWithDuration:1];
id fadeOut = [CCFadeOut actionWithDuration:1];
id fadeInAndOut = [CCRepeatForever actionWithAction:[CCSequence actions:fadeIn, fadeOut, nil]];
[blackScreen runAction:fadeInAndOut];
}
You should use performSelector and get the selector from your NSString using NSSelectorFromString:
[self performSelector:NSSelectorFromString(method)];
instead of [self method];
The standard way is using Selectors as mentioned in Matteo's answer.
You can also look at Objective-C Blocks. They are becoming very common in the CocoaTouch APIs and you can do some very slick things with them. The resulting architecture of your class is often easier to understand IMO.
For example this method from UIView
+ (void)animateWithDuration:(NSTimeInterval)duration
animations:(void (^)(void))animations
completion:(void (^)(BOOL finished))completion
Takes two block, one that is runs the code for the actual animation, and one to for the code after the animation is complete. You could call this with block variables or by writing code inline:
...animations:^{
// animation code
}
completion:^(BOOL finished) {
// completion code
}
The receiving method (in this case animateWithDuration:...) would simply call these blocks at some point like so:
animations();

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

Cocos2d-iPhone: "update" called after dealloc

So I have a subclass of a CCSprite object, and in its init method, I call:
[self scheduleUpdate]
I later release this object from its parent CCNode like so:
[self removeChild:sprite cleanup:YES];
In addition, I call [self unscheduleUpdate] in the sprite's dealloc method.
However, I'm getting a bad memory access, so it appears that the update method is still attempted after the object is released (I've narrowed it down to this, as it works perfectly if I comment out the [self scheduleUpdate] line.
Any ideas?
Found this post in an attempt to ask the same question. I tried unschedule update (within my init method as well) with no luck, but then realized that by moving the [self unscheduleUpdate]; to the actual update method (which is running continuously, unlike the init method) based on a condition it worked!
So, for those looking for copy paste, here's a progress bar example that I'm implementing from http://www.ccsprite.com/cocos2d/using-ccprogresstimer-cocos2d-example.html#HCB_comment_box
-(id) init
{
//initialize progress bar, make sure to add a file named green_health_bar.png to your
//resource folder
timer = [CCProgressTimer progressWithFile:#"green_health_bar.png"];
//set the progress bar type to horizontal from left to right
timer.type = kCCProgressTimerTypeHorizontalBarRL;
//initialize the progress bar to zero
timer.percentage = 0;
//add the CCProgressTimer to our layer and set its position
[self addChild:timer z:1 tag:20];
[timer setPosition:ccp(100, 280)];
[self scheduleUpdate];
}
and in your update method:
-(void)update:(ccTime)dt
{
//get progress bar
CCNode* node = [self getChildByTag:20];
timer.percentage += dt * 10;
if (timer.percentage >= 100)
{
[self gameOver]; //used to stop parallax and show gameover menu
[self unscheduleUpdate];
}
}
I usually don't allow forum reply emails, but feel free to ask questions via #russ152!
Hmm.. try not to use scheduleUpdate? I tried looking for self unscheduleUpdate but there is not such function in a CCNode.. You can try [self unscheduleAllselectors], which stops all selectors of the object , including the update selector, if you are not using the object anymore.. Or use custom selectors instead..

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!

Restarting an action in cocos2D for the iPhone

If I am using an action that does not repeat in cocos2D how do I restart that action?
I am using the code:
CCAnimate* action = [CCAnimate actionWithAnimation:myAnimation];
[mySprite runAction: action];
The action runs fine once, but when an event is triggered I want to be able to run the action again as long as it is finished, so I tried using this when the even is triggered.
if( [action isDone] ){
[mySprite runAction: action];
}
But this results in a crash. Anyone any idea what the correct way to do this is?
try preserving the action in an instance variable. In the header file have a pointer declared
CCAction* myAction;
then when the layer or sprite is initialized
myAction = [CCAnimate actionWithAnimation:myAnimation];
From what point on, whenever you want to call your action do
if( [action isDone] ){
[mySprite runAction: myAction];
}
I think the reason your app is crashing is because you are calling an action that only exists for the duration of the method in which it is initialized.
Im my game i use CCSequences (so i can use CCCallFunc to set/declare variables mid animation), all these CCSequences are stored as instance variables in my CCSprite subclass.
I have an idle animation that repeats for ever.
Whenever I want to 'jump' for instance i call
[self stopAllActions];
[self runAction:jumpSeq];
My jumpSeq is a CCSequence that plays a jump animation, and has a CCCallFunc at the end of the sequence that restarts the idle animation when it is done.
Hope this helps.
Further reading: http://www.cocos2d-iphone.org/wiki/doku.php/prog_guide:actions_special?s[]=cccallfunc
Turns out I just wasn't retaining the action and the sprite must have been deleting it once it was done. So now my code is;
CCAnimate* action = [CCAnimate actionWithAnimation:myAnimation];
[mySprite runAction: action];
[action retain];
and then when i want it to run again;
if( [action isDone] ){
[mySprite runAction: myAction];
}
I had the same issue I declared CCAction* myAction in the header file but when i went to call it from another method i experienced a crash but like #Bongeh mentioned when using [myAction retain] it worked perfectly