CCActionRemove / addChild is not being manifested consistently in scheduled method - ios

Please bear with me because I'm very new to OOP/ObjC/Cocos2d.
I have a method that is triggered every second like so: [self schedule:#selector(eyelidsBlink:) interval:1.0];
The schedule method is this:
-(CCTimer *) schedule:(SEL)selector interval:(CCTime)interval
{
return [self schedule:selector interval:interval repeat:CCTimerRepeatForever delay:interval];
}
The method is below:
- (void)eyelidsBlink:(CCTime)dt{
CCActionRemove *actionRemoveEyelidsNormal = [CCActionRemove action];
[_whiteGuy_EyelidsNormal runAction:actionRemoveEyelidsNormal];
_whiteGuy_EyelidsBlink = [CCSprite spriteWithImageNamed:#"EyelidsBlink_iPhone4.png"];
_whiteGuy_EyelidsBlink.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
[_whiteGuy_EyelidsBlink setScale:0.5];
[self addChild:_whiteGuy_EyelidsBlink];
CCActionRemove *remove_eyelidsBlink = [CCActionRemove action];
[_whiteGuy_EyelidsBlink runAction:remove_eyelidsBlink];
NSLog(#"Eyelids blinked");
_whiteGuy_EyelidsNormal = [CCSprite spriteWithImageNamed:#"EyelidsNormal_iPhone4.png"];
_whiteGuy_EyelidsNormal.position = ccp(self.contentSize.width/2,self.contentSize.height/2);
[_whiteGuy_EyelidsNormal setScale:0.5];
[self addChild:_whiteGuy_EyelidsNormal];
}
I can see the first blink, but I can't see any others after that. My NSLog is printing in the console every second, so I know the eyelidsBlink method is being called.
Can anyone help me figure out why I can't see any blinks after the first? Let me know if you need more information, or if you can suggest any tests to troubleshoot.

There's practically no time for the blink sprite to be rendered because you remove it the instant it was added. You'd have to schedule another selector once, ie eyeLidsBlinkOff that runs 0.1 seconds later and hides the blink sprite.
Note: This code is very inefficient. Creating sprites is a relatively slow operation. Instead keep both sprites as children but set one sprite's visible property to NO. While blinking simply flip each sprite's visible flag. This will make the code a lot shorter too.

Related

How to run the same block of code - Cocos2d

I am using cocos2d and have 2 AI sprites called TheEvilOne and TheEvilTwo. These 2 sprites call the same bloc of code from the same class sending the sprite as a parameter. However the game gets buggy as I run this code as only one of the sprites preforms the actions and my code stops working. My question is is it possible to call the same bloc of code for multiple sprites simultaneously or is there something wrong with my code. Below is an example of what I am running on my program.
-(void)LoadingEvilActions:(id)sender { //This is Getting Constantly Called
if(loaded == NO) {
theEvilOne = [CCSprite spriteWithFile:#"Evil_Alt_Idle_00.png"]; //These sprites have been declared in my .h file
[self addChild:theEvilOne z:200];
theEvilTwo = [CCSprite spriteWithFile:#"Evil_Alt_Idle_00.png"];
[self addChild:theEvilTwo z:200];
loaded = YES;
}
[self CheckCollision:theEvilOne];
[self setCenterofScreen:theEvilOne];
[self StayonScreen:theEvilOne];
[self AiCharacter:theEvilOne];
[self CheckCollision:theEvilTwo];
[self setCenterofScreen:theEvilTwo];
[self StayonScreen:theEvilTwo];
[self AiCharacter:theEvilTwo];
}
-(void)AiCharacter:(id)sender {
CCSprite *EvilCharacter = (CCSprite *)sender;
if (aiactionrunning == NO){
.... Do More Stuff // For E.G.
id jump_Up = [CCJumpBy actionWithDuration:0.6f position:ccp(randomjump, EvilCharacter.contentSize.height)
height:25 jumps:1];
id jump_Down = [CCJumpBy actionWithDuration:0.42f position:ccp(randomjump,-EvilCharacter.contentSize.height)
height:25 jumps:1];
id seq = [CCSequence actions:jump_Up,jump_Down, [CCCallFuncN actionWithTarget:self selector:#selector(stopAiAction:)], nil];
[EvilCharacter stopAllActions];
[EvilCharacter runAction:seq];
aiactionrunning = YES;
}
}
-(void)stopAiAction:(id)sender {
aiactionrunning = NO;
}
EDIT
I'm running random number generators within my AI Character method and many actions.
The Game already works with just 1 enemy sprite and I decided to try and implement a way to create many.
EDIT 2
I just added the part of the method that stops the sprites being constantly, I was trying to simplify all the code that was irrelevant to my question and forgot to add that part.
I changed the way I am calling my methods like suggested by LearnCocos2d not solving my problem but making it much simpler. I am also not using ARC
One of my sprites is the only one preforming the majority of actions however in some instances the other sprite may preform an actions aswell, but is mainly preformed one sprite. I think my main question is can I call the same method using different sprites passing the sprite as a parameter.
EDIT 3
I have figured out that my problem is there is a Boolean value flag that is enclosing my AiCharacter method, where when one sprite runs through the method it stops the other sprite running the method. Is there some way I can implement an array of records or such so each sprite have their own Boolean flags.
With making this 'array' is it possible to change the Boolean for TheEvilOne and TheEvilTwo using the temp sprite EvilCharacter without doing both separately.
If I understand your question correct you are trying to take one action and run it on more than one character at a time, like:
id move = [CCMoveTo actionWithDuration:0.5f position:ccp(0,0)];
[theEvilOne runAction:move];
[theEvilTwo runAction:move];
That wouldn't work because Evil 1 will not have anything performed on it since it was moved to running on Evil 2. When an action is running it is running on one target. Instead you would:
id move = [CCMoveTo actionWithDuration:0.5f position:ccp(0,0)];
[theEvilOne runAction:move];
[theEvilTwo runAction:[move copy]];
Assuming you are using arc. If not then you'd want to release the copy of course. When it comes to your particular example, why are you trying actions to call methods. Why not just call the method? It seems pointless as LearnCocos2D has pointed out.
Another potential problem you have is that you are constantly creating more and more sprites each time the method is called. Is that on purpose or are there only suppose to be one Evil 1 and one Evil 2? If so then you shouldn't be constantly creating more sprites.
Edit:
Remove your bool. Do something like this instead:
CCSprite *EvilCharacter = (CCSprite *)sender;
if ([EvilCharacter numberOfRunningActions] == 0){
...
}

Synchronize SKActions on two or more SKSpriteNode's?

I want to move two (or more) SKSpriteNodes in sync. Any difference will show. I tried to trigger the SKAction for each sprite in order and when the last one is finished it triggers a new move. But it turns out that the actions doesn't end in the same order they are started, which causes a slightly time difference that is noticeable.
Is there a way to run parallel SKActions on two or more sprites with a duration so that they end at exactly the same time or at least in the order they are started?
Here is an principle example of what is not working:
- (void)testMethod1{
SKSpriteNode *child_1=[arrayWithSprites objectAtIndex:1];
SKSpriteNode *child_2=[arrayWithSprites objectAtIndex:2];
//This doesn't work.
[child_1 runAction:[SKAction moveToX:20.0 duration:0.5]];
[child_2 runAction:[SKAction moveToX:20.0 duration:0.5]
completion:^{[self testMethod1];}];
//Actions might not be finished in the order they are started.
}
And here is a way I haven't tried yet but wonder if it might solve my problem:
- (void)testMethod2{
SKSpriteNode *child_1=[arrayWithSprites objectAtIndex:1];
SKSpriteNode *child_2=[arrayWithSprites objectAtIndex:2];
//Will this guarantee total syncronisation?
[self runAction:[SKAction group:[NSArray arrayWithObjects:
[SKAction runBlock:^{[child_1 runAction:[SKAction moveToX:20.0 duration:0.5]];}],
[SKAction runBlock:^{[child_2 runAction:[SKAction moveToX:20.0 duration:0.5]];}],
nil]]
completion:^{[self testMethod2];}];
}
I hope my English and thoughts are understandable.
//Micke....
Just add a container SKNode, and then they will both move at once:
SKNode *containerNode = [[SKNode alloc] init];
[containerNode addChild:node1];
[containerNode addChild:node2]; //add as many as necessary
[containerNode runAction:someAction]; //declare the action you want them both to perform
The solution by Tyler works perfectly.
But if you can't or don't want to do that, you should know that very likely actions are multithreaded and thus they can finish in any order, but they should still finish in the same frame.
To ensure they both ran to completion before running testMethod, you should perform the code in didEvaluateActions. Then you need to find a way to figure out whether both actions have finished. One way is to use a key.
[child_1 runAction:[SKAction moveToX:20.0 duration:0.5] withKey:#"action1"];
[child_2 runAction:[SKAction moveToX:20.0 duration:0.5] withKey:#"action2"];
Then check if both of these actions have run to completion by checking if they still exist:
-(void) didEvaluateActions
{
if ([child_1 actionForKey:#"action1"] == nil &&
[child_2 actionForKey:#"action2"] == nil)
{
// both actions have ended, start new ones here ...
}
}
Alternatively you can use a completion block that both actions run. Increase a NSUInteger counter variable and in the block increase the counter by 1, then test if the counter's value is equal to (or greater) than the number of concurrent actions. If it is, you know that both actions have run to completion:
__block NSUInteger counter = 0;
void (^synchBlock)(void) = ^{
counter++;
if (counter == 2)
{
[self testMethod1];
}
};
[child_1 runAction:[SKAction moveToX:20.0 duration:0.5] completion:synchBlock];
[child_2 runAction:[SKAction moveToX:20.0 duration:0.5] completion:synchBlock];
Is your framerate constant ? Your first example technically should work, however the completion order might be based on something else, like their draw order.
One concept you could employ is to temporarily encapsulate them in a container node, and then move just the container node via an action. When the action is complete remove them from the container and back to their original parent.
Are you finding that they move out of sync visually ? Or does your testMethod1 in some way require that both actions are complete ?
Although the container concept I have suggested will work for your specific example, it's not a valid option unless it's desired that the objects are moving as if they are connected. An example of where the container would not work, is if each object had to move to different locations in 1 second.

background image is not properly scrolling in cocos2d

I write the code for scrolling image in background of game. Simply define two sprite and set their position.
CGSize screenSize=[[CCDirector sharedDirector]winSize];
sky1 = [CCSprite spriteWithFile:#"sky1.png"];
sky1.position=ccp(screenSize.width/2, screenSize.height/2);
[self addChild:sky1];
CCLOG(#"sky1 position width=%f height=%f",sky1.position.x,sky1.position.y);
sky2=[CCSprite spriteWithFile:#"sky2.png"];
sky2.position=ccp(screenSize.width/2, sky1.position.y+sky1.contentSize.height/2+sky2.contentSize.height/2);
[self addChild:sky2];
[self schedule:#selector(scroll:) interval:0.01f];
And write the code for scrolling in scroll method:
-(void)scroll:(ccTime)delta{
CGSize screen=[[CCDirector sharedDirector] winSize];
CCLOG(#"come from schedule method pos.y=%f",sky1.position.y);
sky1.position=ccp(sky1.position.x, sky1.position.y-1);
sky2.position=ccp(sky2.position.x, sky2.position.y-1);
if(sky1.position.y <= -([sky1 boundingBox].size.height));
{
sky1.position=ccp(sky1.position.x, sky2.position.y+[sky2 boundingBox].size.height);
CCLOG(#"COMING IN first ifin scroll mwthod position width=%f pos.y=%f",sky1.position.x,sky1.position.y);
}
if(sky2.position.y<= -[sky2 boundingBox].size.height);
{
sky2.position=ccp(sky2.position.x, sky1.position.y+[sky1 boundingBox].size.height);
CCLOG(#"coming in secnond if ");
}
}
When I remove the if conditions then it works properly for one time. I don't know what is wrong with my condition and what the hell is going on with my code.
Can anybody explain?
I'm not too sure why your code isn't working properly. One thought that pops in my head is your interval may be too short. In other words, 0.01f (1/100) is shorter than your game update speed which is most likely 0.016 (1/60). So first thing I would try is tweaking your interval to maybe 0.02f. You could also drop the interval argument from the schedule call so it just runs once every frame. I would also try dropping the ccTime argument since it isn't being used.
[self schedule:#selector(scroll)];
-(void)scroll {}
But besides all that, this is probably best handled using CCActions. This looks like a simple pair of CCMoveTo actions combined with a CCRepeatForever to get the effect you want. This is the way I would go.
EDIT: here is a bit more detail about using CCActions to accomplish this. There are multiple ways of doing the same thing, but here is one you could try:
float moveDuration = 3; // or however fast you want it to scroll
CGPoint initialPosition = ccp(screenSize.width/2, screenSize.height/2);
CGPoint dp = ccp(0, -1*sky1.contentSize.height); // how far you want it to scroll down
CCMoveBy *scrollSky1 = [CCMoveBy actionWithDuration:moveDuration position:dp];
CCMoveTo *resetSky1 = [CCMoveTo actionWithDuration:0 position:initialPosition];
CCSequence *sequence = [CCSequence actionOne:scrollSky1 two:resetSky1];
CCRepeatForever *repeat = [CCRepeatForever actionWithAction:sequence];
[sky1 runAction:repeat];

Motion Tween effect ( like flash ) on iphone, maybe using cocos 2d?

I thought doing a simple animation would be easy but is is taking hours and Im not getting even close to have the expected effect...
I need to simulate a simple Flash Motion Tween like for iphone/ipad using xcode
this is the desired effect: http://www.swfcabin.com/open/1340330187
I already tried setup a timer adding X position and it doens't get the same effect, my cooworkers suggested me cocos 2d to do this using actions and sprites, which might would be fine although I wouldn't like to third party frameworks, but if there is a way to do the same with cocos I would definitively use it.
Does anybody have any suggestions, I feel like it might be simpler than I thought
thanks all
If there is no troubles to you, that you will have to do it insinge OpenGL view, it is really very simple. To show some info, you need CCLabel class. To change it's position, you need CCMoveTo/CCMoveBy action, to change opacity, you need CCFadeTo/CCFadeIn/CCFadeOut actions, to make delay you need CCDelayTime. To make it all work together you need CCSpawn and CCSequence.
CCSpawn will run several actions at the same time(for example fade in and move from right to the center), CCSequence will run several actions one by one (sequence to fade in + move to center, delay for same time, sequence to fade out + move from center to the left). Then you should only schedule method, that will create labels and run actions on them. In code it will be something like
lets define full animation time
#define ANIMATION_TIME 4.f
schedule method in any place you want to start animation
[self schedule:#selector(runNextMessage) interval:ANIMATION_TIME];
it will call runNextMessage method every ANIMATION_TIME seconds
- (void) runNextMesage
{
NSString* message = //get next message
CCLabelTTF* label = [CCLabelTTF labelWithString:message
dimensions:desiredDimensionsOfTheLabel
alignment:UITextAlignmentLeft
lineBreakMode:UILineBreakModeWordWrap
fontName:#"Arial"
fontSize:20.f];
CGSize winSize = [[CCDirector sharedDirector] winSize];
// place the label out the right border
[label setPosition: ccp(winSize.width + label.contentSize.width, winSize.height / 2)];
// adding it to the screen
[self addChild:label];
ccTime spawnTime = ANIMATION_TIME / 3;
// create actions to run
id appearSpawn = [CCAction actionOne:[CCMoveTo actionWithDuration:spawnTime]
two:[CCFadeIn actionWithDuration:spawnTime]];
// create show action and disappear action
// create result sequence
id sequence = [CCSequence actions: appearSpawn, showAction, disappearAction, nil];
[label runAction: sequence];
}

animated sprite won't stop animation - cocos2d

I have an animated sprite using two pngs. The animation works fine. I have another method that is run when the game is over.
//Grey mouse with Pompom
greyMousePomPom = [CCSprite spriteWithFile:#"pink_mice_pom_anime_01.png"];
greyMousePomPom.tag=132;
[self addChild:greyMousePomPom z:6];
greyMousePomPom.position = CGPointMake(550, 70);
//Grey Pom Pom Mouse animation
CCAnimation *greyMousePomPomAnimate = [CCAnimation animation];
[greyMousePomPomAnimate addFrameWithFilename:#"gray_mice_pom_anime_01.png"];
[greyMousePomPomAnimate addFrameWithFilename:#"gray_mice_pom_anime_02.png"];
id greyMousePopPomAnimationAction = [CCAnimate actionWithDuration:1.3f animation:greyMousePomPomAnimate restoreOriginalFrame:NO];
repeatAnimationPomPom2 = [CCRepeatForever actionWithAction:greyMousePopPomAnimationAction];
[greyMousePomPom runAction:repeatAnimationPomPom2];
When I run my method to change the animated sprites texture and to stop them the animation continues behind the new texture.
-(void) changePomPomMiceToSadFaceForFreeFall
{
NSLog(#"making the mice sad");
[self stopAllActions];
[greyMousePomPom setTexture:[[CCTextureCache sharedTextureCache] addImage:#"gray_mice_pom_anime_03.png"]];
}
I know this method is working because it is NSLogging and the textures are changing. But why aren't the animations stopping? I have tried to remove it by tag and by declaring the action but no success.
I know there are a lot of people out there that are smarter than me.. can you help?
What you're doing right now is stop all animations added to the current node:
self
If you added any action to self, this command would be perfectly fine to stop all of them.
Instead what you need to do is, you need to call the stopAllActions method on the object you added the actions to:
[greyMousePomPom stopAllActions];
HTH

Resources