How to pause a game in cocos2d? - objective-c

I know this question has been asked and answered before but none of the answers seem to fit what I am trying to do. I need to be able to pause animations but still be able to have my pause menu come up and ask the user for input. Is this possible and if so how do I go about it.

There are 2 methods pause and resume for this purpose in CCDirector:
[[CCDirector sharedDirector] pause];
and
[[CCDirector sharedDirector] resume];
From the documentation:
The running scene will be drawed but all scheduled timers will be paused While paused, the draw rate will be 4 FPS to reduce CPU consumption

I suggest you to push the "pause scene" on the cocos2d scene stack.
CCScene * pauseScene = ...;
[[CCDirector sharedDirector] pushScene:pauseScene];
This will automatically pause your current scene (which I assume is the level scene) and will run the "pause scene" so that user can interact with it.
It is very important to remember that after the instructions above have been executed, you will have 2 scenes in the scenes stack of cocos2d (the level scene [frozen] + the pause scene [running]).
So, once you are in the "pause scene", if you want resume the level you just need to pop the pause scene from the stack.
[[CCDirector sharedDirector] popScene];
This will automatically remove the "pause scene" and resume the previous scene in the stack (which is the level scene).
Please remember that you will always need to "pop" the pause scene sometime. It is important not to forget the "level scene" is still in the scene stack of cocos2d!
E.g.: if in the "Pause Scene" you have a button to load the level selection menu and permanently leave the current level, you should implement it as follow:
CCScene * levelSelector = ...
[[CCDirector sharedDirector] popScene]; // to remove the "pause scene" from the stack
[[CCDirector sharedDirector] replaceScene:levelSelector]; // replace the "level scene" with the "levelSelector scene"

I tried all of the suggestions but none of them worked in my case, so this is what I ended up doing which worked really well for me. This is to pause.
[self pauseSchedulerAndActions];
for (CCNode *child in [self children]) {
[child pauseSchedulerAndActions];
}
And this to resume:
[selfReference resumeSchedulerAndActions];
for (CCNode *child in [selfReference children]) {
[child resumeSchedulerAndActions];
}

With Cocos 2D Version 2 the action manager is no more a singleton. But rather the code has been fused with the CCDirector.
So this is the way to pause all actions in a particular scene and resume them as well.
-(void)pauseGamePlayScene{
for(CCSprite *sprite in [self children]) {
[[[CCDirector sharedDirector] actionManager]pauseTarget:sprite];
}
}
-(void)resumeGamePlayScene{
for(CCSprite *sprite in [self children]) {
[[[CCDirector sharedDirector] actionManager]resumeTarget:sprite];
}
}
Cocos 2d Ver 2 Action Manager Documentation
Hope this helps.

Stopping just animations?
[ [ CCDirector sharedDirector ] stopAnimation ];
[ [ CCDirector sharedDirector ] startAnimation ];
If you want to stop everything, pause as you have seen done everywhere else (after the menu has been moved into place), then unpause once the user touches a certain thing.

I am new to cocos2D and have used seperate game-loop (rather than scheduleupdate) and am unscheduling it along with some more schedulers. It would automatically pause the gameplay at the same time i am placing a new layer (i.e. Pauselayer) ontop of the gamelayer.
Appears pretty complex, but worked for me.

I use:
#interface CCNode (PauseAdded)
#end
#implementation CCNode (PauseAdded)
-( void ) traversePause
{
CCNode* a = [children_ lastObject];
CCARRAY_FOREACH( children_, a )
{
[scheduler_ pauseTarget:a];
[actionManager_ pauseTarget:a];
[a traversePause];
}
[scheduler_ pauseTarget:self];
[actionManager_ pauseTarget:self];
}
-( void ) traverseResume
{
CCNode* a = [children_ lastObject];
CCARRAY_FOREACH( children_, a )
{
[a traverseResume];
[scheduler_ resumeTarget:a];
[actionManager_ resumeTarget:a];
}
[scheduler_ resumeTarget:self];
[actionManager_ resumeTarget:self];
}
#end
That way you can just do something like this assuming your game layer is separated from the pause layer:
[scene.game_layer traversePause];
And to unpause:
[scene.game_layer traverseResume];

Try this PauseButton class https://github.com/iabtyagi/custom-pause-cocos2d .
does exactly what you are looking for.

CCPhysicsNode *_physics;
_physics.paused = true;// It will pause your game but not actions.
_physics.paused = false;// It will resume your spinning and falling of sprites while button is pressable like during pause game.

Related

Is it possible to use cocos2d transitions on a CCNode?

Every example I see on how to use transitions is with CCScene like this:
[[CCDirector sharedDirector] replaceScene:[CCTransitionCrossFade transitionWithDuration:0.5f scene:[NewScene scene]]];
My app is setup with a GameScene, MenuScene ..etc. Within the GameScene I setup the DPad and the HUD and under that I have the hero moving to different levels which are of type CCNode. I want to use the native transitions to move from one level to the next.
I found a question similar to what I need here but that user was essentially told to build the transitions himself.
The confusing part to me is that CCNode contains a method called:
- (void) onEnterTransitionDidFinish
So, is there a way to use transitions with CCNode that I'm missing? Is my app setup incorrectly and the levels should be CCScene? If so how to I keep the HUD and Dpad over the transition?
Thanks in advance for any help.
CCTransitions are specifically for scenes. CCNodes contain onEnterTransitionDidFinish, as it gets called on every CCNode child of a CCScene. Fortunately, you can just check the CCTransition code and see how they are implemented and then roll your own for layers (many have done and shared this).
For example, I wrote a simple little method which will use blocks to give the same fade-in, fade-out effect as a scene transition, but for an individual layer (along with some added features). Just pass in the layer you want to cover up, the speed of the fade out and fade in, the color to fade to, and the block you wish to execute while the layer is hidden.
-(void)fadeLayer:(CCLayer*)layer withOutDuration:(float)outTime inDuration:(float)inTime color:(ccColor3B)color withBlock: (void(^)())block
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCLayerColor *toplayer = [CCLayerColor layerWithColor:ccc4(color.r, color.g, color.b, 0) width:winSize.width height:winSize.height];
[layer addChild:toplayer z:INT_MAX];
[toplayer runAction:
[CCSequence actions:
[CCFadeIn actionWithDuration:outTime],
[CCCallBlock actionWithBlock:block],
[CCFadeOut actionWithDuration:inTime],
[CCCallBlockN actionWithBlock:^(CCNode *node) {
[node removeFromParentAndCleanup:YES];
}],
nil]
];
}

Cocos2d scene loaded with wrong orientation

I'm trying to make a cocos2d scene on top of some UIKit views. I understand that the cocos2d 2.0 CCDirector is a subclass of UIViewController so that I can just add that as a subview.
I've written this function to handle switching to a scene in my appDelegate (currently thats
the delegate of my MainWindow.xib following http://www.raywenderlich.com/4817/how-to-integrate-cocos2d-and-uikit):
- (void)switchToScene
{
self.viewController = [CCDirector sharedDirector];
[[CCDirector sharedDirector] view].center = self.window.center;
[self.window addSubview:[self.viewController view]];
[[CCDirector sharedDirector] runWithScene:[CCTransitionSlideInL transitionWithDuration:1 scene:[BattleLayer scene]]];
}
So I'm missing quite a bit from this code. But with this, I get some resemblance of a scene to load http://i.stack.imgur.com/pubWE.png
My question is:
1: If I'm going in the right direction
2: If it is, is the next step to manually adjust the orientation of the scene?
2.1: How do I do this :)
I'm not sure how you want it to look but it seems that the CCDirector's view is rotated 90 degrees CW. This may be because of a few reasons:
you add the view wrong (i.e. rotated 90 degrees CW)
you rotated the device and the CCDirector didn't rotate (in which case you should look at orientation parameters and who and what rotates. It may be that the app rotates but CCDirector doesn't receive the rotation flag).
you can rotate the CCDirector view with CGAffineTransformRotate (i'm not sure..but because CCDirector is a subclass of UIViewController..it should work).
I solved the issue by creating a UINavigationController
- (void)switchToScene:(CCScene *)sceneObj
{
navController_ = [[UINavigationController alloc] initWithRootViewController:director_];
[window_ setRootViewController:navController_];
[[CCDirector sharedDirector] pushScene:sceneObj];
}

Disable double tap zoom in MKMapView (iOS 6)

in ios 5 i was able to disable the double tap zoom by just overriding it with a new double tap gesture. But it seems that the double tap gesture is no longer in the gesturerecognizer array that comes with the mkmapview.
NSArray *gestureRecognizers = [_mapView gestureRecognizers];
for (UIGestureRecognizer *recognizer in gestureRecognizers) {
NSLog(#"%#", recognizer);
}
returns nothing in ios 6, where in ios 5 it would return 2 recognizers, one for single tap and one for double tap.
I'd look through the gesture recognizers of MKMapView's subviews. It's probably still there somewhere.
Of course, messing around with another view's GRs is slightly dubious and will likely break the next time Apple changes something about MKMapView...
EDIT: For the benefit of anyone else reading this, please check that it's a UITapGestureRecognizer and that numberOfTapsRequired == 2 and numberOfTouchesRequired == 1.
Also, instead of disabling double-taps on the map entirely, consider adding a double-tap GR on the annotation and then do [mapDoubleTapGR requireGestureRecognizerToFail:annotationDoubleTapGR]. Again, hacky — don't blame me if it breaks on the next OS update!
This worked for me:
[_mapView.subviews[0] addGestureRecognizer:MyDoubleTapOverrider];
Do you want to let the user do anything with the view? If not, it sufficient to set userInteractionEnabled to NO. If so, what specific interactions do you need to allow? Everything but double-tapping? Why disable that one interaction?
The more we know about your use case, the better the answers we can provide.
This works for me:
//INIT the MKMapView
-(id) init{
...
[self getGesturesRecursive:mapView];
...
}
And then let the recursive function loop through the subviews and find the GR:s.
-(void)getGesturesRecursive:(UIView*)v{
NSArray *gestureRecognizers = [v gestureRecognizers];
for (UIGestureRecognizer *recognizer in gestureRecognizers) {
if ([recognizer isKindOfClass:[UITapGestureRecognizer class]]) {
[v removeGestureRecognizer:recognizer];
}
}
for (UIView *v1 in v.subviews){
[self getGesturesRecursive:v1];
}
}
This example removes all tap-GR:s. But I guess you can specify to remove whatever you'd like.
You can use a long tap gesture instead, that works.

Making a sprite move randomly across the screen

I'm making a 2d Game, in which I need instances of a sprite to fly randomly across the screen. They will spawn in randomly just beyond the boundaries of the iPhone screen, then move within the screen. When they hit the edges, they will appear back on the other side. All I need to know is how to get the sprite to move randomly.
Add this method to your layer class - it takes in a sprite and then moves it randomly around the screen for ever:
-(void)moveRandom:(CCSprite*)s
{
CGPoint randomPoint = ccp(arc4random()%480, arc4random()%320);
NSLog(#"%#", NSStringFromCGPoint(randomPoint));
[s runAction:
[CCSequence actions:
[CCMoveTo actionWithDuration:arc4random()%5+1 position: randomPoint],
[CCCallBlock actionWithBlock:^{
[self performSelector:#selector(moveRandom:) withObject:s afterDelay:0.5];
}],
nil]
];
}
As you can see it's pretty easy - generate a random point on the screen and then run move action on the sprite to that point. When that's done - just repeat.
To add a sprite on the screen and start the process, put this (probably) in your scene init method or wherever you do the scene initialization:
CCSprite* s = [CCSprite spriteWithFile:#"yourImage.png"];
[self addChild: s];
[self moveRandom:s];

Animated UIImageView which should recognize taps on it (at every location the UIImageView is at)

I'm making an iPhone App which involves balloons (UIImageViews) being spawned from the bottom of the screen and moving up to the top of the screen.
The aim is to have the UIImageViews set up so that a tap gesture on a balloon causes the balloons to be removed from the screen.
I have set up the GestureRecognizer and taps are being recognized.
The problem I am having is that the animation method I am using does not provide information about where the balloon is being drawn at a specific point in time, and treats it as already having reached the final point mentioned.
This causes the problem of the taps being recognized only at the top of the screen(which is the balloon's final destination).
I have pasted my code for animation here:
UIViewAnimationOptions options = UIViewAnimationOptionBeginFromCurrentState|UIViewAnimationOptionAllowUserInteraction;
[UIView animateWithDuration:2.0 delay:0.0 options:options animations:^{
balloonImageView.center = p;
} completion:^(BOOL finished) {
if (finished) {
balloonImageView.hidden= TRUE;
balloonImageView.image=nil;
}
I keep track of where an image is by giving a unique tag to a UIImageView and checking if gesture.view.tag = anyBalloonObject.tag and if it does I destroy the balloon.
My basic question is, is there another animation tool I can use? I understand that I can set a balloon's finish point to slightly higher than where it is on screen until it reaches the top but that seems like it is very taxing on memory.
Do you have any recommendations? I looked through the Core Animation reference, and didn't find any methods that suited my needs.
In addition will the Multi-threading aspects of UIViewAnimation affect the accuracy of the tap system?
Thank You,
Vik
You shouldnt animate game-objects (objects you interact with while they are moving) like that.
You should rather use a NSTimer to move the ballons.
-(void)viewDidLoad {
//Set up a timer to run the update-function
timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(update) userInfo:NULL repeats:YES];
}
-(void) update {
for (int i = [balloonArray count] - 1; i >= 0; i--) { //Loop through an array containing all your balloons.
UIImageView *aBalloon = [balloonArray objectAtIndex:i]; //Select balloon at the current loop index
aBallon.center = CGPointMake(aBallon.center.x, aBalloon.center.y + 1); //Change the center of that balloon, in this case: make it go upwards.
}
}
Here is what you need to declare in .h:
NSTimer *timer;
NSMutableArray *ballonArray;
//You should use properties on these also..
You will have to create a spawn method which adds balloons to the screen AND to the array.
When you have done that, it's simple to implement the touch methods, You can just loop trough the balloons and check:
//You need an excact copy of the loop from earlier around this if statement!
If (CGRectContainsPoint(aBalloon.frame, touchlocation)) { //you can do your own tap detection here inthe if-statement
//pop the balloon and remove it from the array!
}
Hope that isnt too scary-looking, good luck.