Restarting an action in cocos2D for the iPhone - 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

Related

How can I delay my aliens' laser shots to fire shortly after they enter the view?

As my app stands right now, each alien fires once immediately once it enters the view. Based on the code below, how can I edit to allow each alien to shoot shortly after it enters the view instead of immediately upon [self addChild...] ?? I'm not picky on time timing per say, I'd just like to randomize it a bit. Thanks!
alienLaser = [_alienLasers objectAtIndex:_nextAlienLaser];
_nextAlienLaser++;
if (_nextAlienLaser >= _alienLasers.count) _nextAlienLaser = 0;
alienLaser.position = ccpAdd(alien.position, ccp(alienLaser.contentSize.width/8, 0));
alienLaser.visible = YES;
[alienLaser stopAllActions];
[alienLaser runAction:[CCSequence actions:
[CCMoveBy actionWithDuration:300 position:ccp(winSize.width, -100000)],
[CCCallFuncN actionWithTarget:self selector:#selector(setInvisible:)],
nil]];
Don't use sleep. That would be a horrible idea. Run a search for 'sleep' and see what they say about it. Instead, just create a wait action. Then add that action to your sequence along with others.
SKAction *wait = [SKAction waitForDuration:0.5];
SKAction *seq = [SKAction sequence:#[wait,shoot,move]];
[alienLaser runAction:seq];

Play animation after another animation on sprite

I've looked all over and i can't find any information on how to do this. I want to run an action animation then right after that animation is done i want it to run another action animation all on the same sprite. how is this possible?
[self.mainShip runAction:retractdoor];
[self.mainShip runAction:activatedoor];
this crashes me.
self.mainShip runAction: [CCSequence actions:retractdoor,activatedoor, nil];
gives me a yellow notification
incompatible pointer types sending Cc action to parameter of type ccfinite time action
CCAnimation *retractdoorAnimation = [CCAnimation
animationWithSpriteFrames:retractdoorframes delay:0.1f];
CCAnimation *activatedoorAnimation = [CCAnimation
animationWithSpriteFrames:activatedoorframes delay:0.1f];
self.retractdoorAction = [CCAnimate actionWithAnimation:retractdoorAnimation];
self.activatedoorAction = [CCAnimate actionWithAnimation:activatedoorAnimation];
you missed the nil termination.
[self.mainShip runAction: [CCSequence actions:retractdoor,activatedoor, nil]];
this should work, dont use the square brackets and dont miss the comma..

Removing animation sprite frames from layer?

Let's say I have a character in a game and its class is like this.
#interface Player
{
CCSprite* stand;
CCAnimation* run;
}
-(void) playRunAction
{
// Create CCAnimate* object from CCAnimation object (run)
[self runAction:runAniate];
}
-(void) playStandAction
{
stand.visible = YES;
[self stopAllActions];
}
The player has ability to stand or run.
But one problem is, after playStandAction is called, stand animation is visible and running animation stopped, but one frame of running animation still there!
( Now you see 'stand sprite' AND 'one of running animation frame' together. )
How can I make running animation not visible?
P.s Can anyone throw me a better way of managing animation in one character? This is totally disaster as animations added.
-(void) playStandAction
{
//Make the animation object.visible = NO; here
stand.visible = YES;
[self stopAllActions];
}
and in
-(void) playRunAction
{
// Create CCAnimate* object from CCAnimation object (run)
//Make the animation object.visible = YES; here
stand.visible = NO;
[self runAction:runAniate];
}
Use method with parameter restoreOriginalFrame and pass it yes
I don't know which method you are calling for creating CCAnimate object...
Like this:
[CCAnimate actionWithAnimation:animation restoreOriginalFrame:YES]];
And don't call runAction on layer. I would prefer you to runAction on sprite itself...
You don't need to hide and show 2 different objects...
Hope this helps. :)

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..

Replacing Scene and Stop All Animations and Sounds

I have a home button on my scene which when pressed goes to the home menu. I use replaceScene to replace the current scene (Game Scene) with the HomeMenu Scene. For some reason the actions and sounds which are happening in the game scene are not stopped when I replace the scene. I have tried the following code but still when I am in the home menu I can hear the actions and sounds of the game scene playing.
// fired when the home menu is
clicked!
-(void) homeMenuClicked:(CCMenuItem *) item { NSLog(#"home menu clicked!");
CCScene *scene = [[CCDirector
sharedDirector] runningScene]; [scene
stopAllActions];
[self.layer stopAllActions];
[self unloadSoundEffects];
[[CCDirector sharedDirector]
replaceScene:[CCSlideInLTransition
transitionWithDuration:1.0
scene:[HomeScene scene]] ];
}
I must also add that the game layer also has a timer (NSTimer) object which starts in 2 seconds or something.
UPDATE 2:
Let me post some code! I think the problem is that when the player guess the correct answer the following method is invoked:
[self updateForCorrectAnswer];
Inside updateForCorrectAnswer I have a performSelector which is scheduled to fire in 6-7 seconds. I believe that performSelector is the culprit. If somehow can I stop that from being firing then the I think I will be fine.
[self performSelector:#selector(refreshScore) withObject:nil afterDelay:7.0];
You should not use NSTimer as cocos2d documents.
/* call scheduleUpdate in initializing */
[self scheduleUpdate];
It schedules update: method to be called every frame when this node is on the stage.
- (void)update:(ccTime)dt
{
/* This method is automatically called every frame. */
}
scheduleUpdateWithPriority:, schedule:interval: are also available.
And why don't you use like this instead of performSelector:withObject:afterDelay.
[self runAction:[CCSequence actions:[CCDelayTime actionWithDuration:7], [CCCallFunc actionWithTarget:self selector:#selector(refreshScore)], nil]];
If you use this method "[self performSelector:withObject:afterDelay:]", cocos2d will not manage it in this run loop. You should use it instead of the previous one:
[self schedule:interval:];
Then in your "homeMenuClicked:" method, just call this:
[self.layer unschedule:#selector(refreshScore)];
I haven't tried it but I think it'll be better.
For more information you can see the documentation here.