I have a gameplay scene, over which I add a CCNode as a child. My Game Over node has a replay CCButton in it.
The button is supposed to restart the game play scene. The problem is, when I press the "Restart" button, it goes through the lines but it doesn't perfrom replaceScene. Also it doesn't highlight when pressed. Here's my relevant code:
The code where I add the Game Over Node in my GamePlay Class (.m):
CCNode GameOver = [[GameOverNode alloc] init];
[self unscheduleAllSelectors];
[self stopAllActions];
[[OALSimpleAudio sharedInstance] stopBg];
[[CCDirector sharedDirector] stopAnimation];
[[CCDirector sharedDirector] pause];
[self addChild:GameOver z:5];
and here's the code for GameOver Class (.h):
#interface GameOverNode:CCNode {
CCButton *_aButton;
}
#property (nonatomic, retain) CCButton *aButton;
- (id)init;
- (void)ButtonPressed:(id)sender;
and Game Over (.m):
-(id)init {
if ( self = [super init] ){
CCSpriteFrame *replayFrame = [CCSpriteFrame frameWithImageNamed:#"Replay.png"];
_aButton = [CCButton buttonWithTitle:#"" spriteFrame:replayFrame];
_aButton.position = ccp(200,200);
[_aButton setTarget:self selector:#selector(ButtonPressed:)];
[self addChild:_aButton z:2];
}
return self;
}
- (void)ButtonPressed:(id)sender
{
NSLog(#"Button pressed");
CCTransition* t = [CCTransition transitionFadeWithDuration:0.4f];
t.outgoingSceneAnimated = YES;
t.incomingSceneAnimated = YES;
[[CCDirector sharedDirector] replaceScene:[GamePlayScene scene] withTransition:t];
}
The thing is, it prints out "Button pressed", also goes through the rest of the code of the method, but nothing happens.
I'll appreciate if you can let me know what I am doing wrong.
Thanks!
It does not work because you have paused the CCDirector. Remove the following line:
[[CCDirector sharedDirector] pause];
Alternatively if you really need that, resume the director before you attempt to replace the scene.
[[CCDirector sharedDirector] resume];
[[CCDirector sharedDirector] replaceScene:[GamePlayScene scene] withTransition:t];
Related
I am loading a cocos2d game inside an Storyboard. I implemented a CocosViewController that loads the cocos2d scene (IntroLayer) and IntroLayer that starts the game. I would like to go back to CocosViewController or open a new viewcontroller when the timer (countdown) is zero in the game.
I tried calling [[CCDirector sharedDirector] popScene]; when the countdown arrives to zero and with CocosViewController *cocos = [[CocosViewController alloc] init]; [cocos.navigationController dismissViewControllerAnimated:YES completion:nil];
in onExit method but the app crashes when the countdown arrives to zero, any suggestion?
My CocosViewController set up cocos2d
-(void)setupCocos2d {
CCDirector *director = [CCDirector sharedDirector];
//[[[self navigationController] navigationBar] setHidden:YES];
if([director isViewLoaded] == NO)
{
// Create the OpenGL view that Cocos2D will render to.
CCGLView *glView = [CCGLView viewWithFrame:self.view.bounds
pixelFormat:kEAGLColorFormatRGB565
depthFormat:0
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
// Assign the view to the director.
director.view = glView;
// Initialize other director settings.
[director setAnimationInterval:1.0f/60.0f];
[director enableRetinaDisplay:YES];
}
// Set the view controller as the director's delegate, so we can respond to certain events.
director.delegate = self;
// Add the director as a child view controller of this view controller.
[self addChildViewController:director];
// Add the director's OpenGL view as a subview so we can see it.
[self.view addSubview:director.view];
[self.view sendSubviewToBack:director.view];
// Finish up our view controller containment responsibilities.
[director didMoveToParentViewController:self];
// Run whatever scene we'd like to run here.
NSArray *parameters = [[NSArray alloc] initWithObjects:#"3", #"sound", #"countdown", nil];
if(director.runningScene)
[director replaceScene:[IntroLayer scene];
else
[director pushScene:[IntroLayer scene];
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"cocos2d controller viewdidload");
[self setupCocos2d];
}
-(void)viewDidUnload {
NSLog(#"did unload");
[[CCDirector sharedDirector] setDelegate:nil];
[[CCDirector sharedDirector] end];
}
IntroLayer creates the scene for the game. This is my onExit method:
-(void) onExit{
NSLog(#"Introscene exit");
[super onExit];
}
And this is the game. I want to load a viewcontroller when the game finished.
-(void)update:(ccTime)dt{
if (myTime > 0) {
myTime -= (float)dt;
[timeLabel setString:[NSString stringWithFormat:#"%.2f", myTime]];
} else {
myTime = 0;
[timeLabel setString:[NSString stringWithFormat:#"0.00"]];
[[CCDirector sharedDirector] popScene];
}
if (clicks < currentClicks) {
clicks = currentClicks;
//[clicksLabel setString:[NSString stringWithFormat:#"%i", clicks]];
}
}
-(void) onExit
{
[super onExit];
[[CCDirector sharedDirector] stopAnimation];
CocosViewController *cocos = [[CocosViewController alloc] init];
[cocos.navigationController dismissViewControllerAnimated:YES completion:nil];
// [cocos.navigationController popViewControllerAnimated:YES];
//AppDelegate *app = (AppDelegate *)[[UIApplication sharedApplication] delegate];
//[app.navController popViewControllerAnimated:YES];
//dismissModalViewControllerAnimated:YES
NSLog(#"game exit");
}
For pop IntroLayer To ViewConrtroller As below code
[[[CCDirector sharedDirector] navigationController] popViewControllerAnimated:YES];
I have created a custom class LevelScoreCard:CCNode which have some CCMenuItemImages.
Here are the interface and implementation details
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface LevelScoreCard : CCNode {
#private CCSprite *levelScoreBackGround;
#private CGSize winSize;
}
-(id)initForLevelNo:(int)levelNo withWiningStatus:(BOOL)won;
#end
and
#import "LevelScoreCard.h"
#import "TestLayer3.h"
#implementation LevelScoreCard
-(id)initForLevelNo:(int)levelNo withWiningStatus:(BOOL)won
{
if( (self=[super init]) ) {
winSize=[CCDirector sharedDirector].winSize;
//Adding the BackGround
levelScoreBackGround=[CCSprite spriteWithFile:#"levelScoreBackGround.png"];
self.contentSize=levelScoreBackGround.contentSize;
[self addChild:levelScoreBackGround ];
//Adding the Game Level-Wining Status
CCSprite *winingStatus;
if (won) {
winingStatus=[CCSprite spriteWithFile:#"levelCompleted.png"];
}
else{
winingStatus=[CCSprite spriteWithFile:#"levelFailed.png"];
CCSprite *skull=[CCSprite spriteWithFile:#"skull.png"];
skull.position=ccp(0,-self.contentSize.height*0.07);
[self addChild:skull];
}
winingStatus.position=ccp(0,self.contentSize.height*0.32);
[self addChild:winingStatus];
CCMenuItem *homeButton = [CCMenuItemImage itemWithNormalImage:#"home1.png"
selectedImage:#"home1.png"
block:^(id sender) {
printf("\nHome button clicked");
}];
CCMenuItem *replayButton = [CCMenuItemImage itemWithNormalImage:#"replay.png"
selectedImage:#"replay.png"
block:^(id sender) {
printf("\nReplay button clicked");
}];
CCMenuItem *nextButton;
CCMenu *menu;
NSDictionary *nextLevelInfo=[self gameInformationForLevel:levelNo+1];
//Create a menu from the button and center it on the screen
if (![[nextLevelInfo objectForKey:#"isLocked"] intValue]) {
nextButton = [CCMenuItemImage itemWithNormalImage:#"next.png"
selectedImage:#"next.png"
block:^(id sender) {
printf("\nNext button clicked");
}];
menu = [CCMenu menuWithItems:homeButton, replayButton,nextButton, nil];
[menu alignItemsHorizontallyWithPadding:60];
}
else
{
menu = [CCMenu menuWithItems:homeButton, replayButton, nil];
[menu alignItemsHorizontallyWithPadding:20];
}
menu.position = ccp(0,-self.contentSize.height*0.35);
//Add the menu as a child to this layer
[self addChild:menu];
}
return self;
}
-(NSDictionary*)gameInformationForLevel:(int)levelNo
{
//---Retrieving Information About Levels----
NSMutableDictionary *gameInfo=[[NSUserDefaults standardUserDefaults] objectForKey:#"gameInfo"];
NSString *levelKey=[NSString stringWithFormat:#"level%dInfo",levelNo];
return [[gameInfo objectForKey:levelKey] copy];
}
#end
When i am using this LevelScoreCard from simple CCLayer which is loaded directly from the IntroLayer,the blocks associated with the MenuItems are working perfectly.
But when I am using it in another CCLayer which is loaded conditionally from a second CCLayer, StartLayer with the following code,MenuItems are not responding
-(void)loadLevelAfterDelay:(ccTime)dt
{
#warning Game has 4 levels, implement game layer 2,3,4 after fixing the story
switch (levelSelected) {
case 1:
{
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.50 scene:[Level1 scene] ]];
break;
}
case 2:
{
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.50 scene:[TestLayer2 scene] ]];
break;
}
case 3:
{
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.50 scene:[Level1 scene] ]];
break;
}
case 4:
{
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:0.50 scene:[Level1 scene] ]];
break;
}
default:
break;
}
}
The problem was that the previous layer StartLayer:CCLayer had a SettingsMenu:CCNode which was registered to to the touchDispatcher as
[[CCDirector sharedDirector].touchDispatcher addTargetedDelegate:self priority:INT_MIN+2 swallowsTouches:YES];
This was the culprit to swallow touches before they are delivered to CCMenuItemImages.So ,i learned a great lesson which says never register any CCNode type item which will swallow touch.
It's good habit not to swallow touches while registering with touchDispatcher with the following piece of code
[[CCDirector sharedDirector].touchDispatcher addTargetedDelegate:self priority:INT_MIN+2 swallowsTouches:NO];
I’m having a real headache trying to diagnose this problem for quiet awhile now.
Details:
I have the following nodes:
GameLayer; the main game layer, also a semi-singleton.
HUDLayer; a simple HUD layer (for score and pause button), added to the scene along with GameLayer of course.
PauseLayer; a CCLayerColor subclass which holds a simple menu (resume, restart, and main menu).
The PauseLayer is added to the GameLayer as a child whenever the pause button is pressed.
Problem:
Restarting the game/scene works fine by: [self restartGame] from within GameLayer itself.
But invoking the same method from PauseLayer by using: [[GameLayer sharedGameLayer] restartGame] does not completely replace the GameLayer scene with a new instance; old scene is still there with no childs, same old score, etc, albeit GameLayer’s dealloc is being called (Only if no transition is used).
Code:
GameLayer
static GameLayer *sharedGameLayer;
#implementation GameLayer
- (id)init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super's" return value
if(self = [super initWithColor:ccc4(255, 255, 255, 255) width:[CCDirector sharedDirector].winSize.width height:[CCDirector sharedDirector].winSize.height])
{
NSLog(#"%s", __PRETTY_FUNCTION__);
sharedGameLayer = self;
.
.
}
+ (GameLayer *)sharedGameLayer {
#synchronized(self) {
if (sharedGameLayer == nil) {
sharedGameLayer = [[self alloc] init];
}
}
return sharedGameLayer;
}
- (void)pauseGame
{
NSLog(#"%s", __PRETTY_FUNCTION__);
if (![[CCDirector sharedDirector] isPaused])
{
// Disable the top menu.
self.hud.topMenu.enabled = NO;
// Pause game.
[[CCDirector sharedDirector] pause];
// Pause the background music.
[[SimpleAudioEngine sharedEngine] pauseBackgroundMusic];
// Disable touch detection.
[[CCDirector sharedDirector].touchDispatcher removeDelegate:self];
// Add the Pause Layer.
PauseLayer *pauseLayer = [[PauseLayer alloc] init];
pauseLayer.tag = 2;
pauseLayer.zOrder = 10;
[self addChild:pauseLayer z:10 tag:2];
}
}
- (void)restartGame
{
NSLog(#"%s", __PRETTY_FUNCTION__);
[[NSNotificationCenter defaultCenter] removeObserver:self];
[[CCDirector sharedDirector].touchDispatcher removeAllDelegates];
[self stopAllActions];
[self unscheduleAllSelectors];
[self unscheduleUpdate];
[self removeAllChildrenWithCleanup:YES];
[[CCDirector sharedDirector] replaceScene:[GameLayer scene]];
//[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[GameLayer scene]]];
}
- (void)dealloc
{
NSLog(#"%s", __PRETTY_FUNCTION__);
sharedGameLayer = nil;
}
#end
PauseLayer
- (id)init
{
if (self = [super initWithColor:ccc4(0, 0, 0, 128) width:[CCDirector sharedDirector].winSize.width height:[CCDirector sharedDirector].winSize.height])
{
// Create a menu.
// Resume button.
CCLabelTTF *resumeLabel = [CCLabelTTF labelWithString:#"Resume" fontName:#"Marker Felt" fontSize:32];
resumeLabel.color = ccc3(240, 240, 240);
CCMenuItemLabel *resumeButton = [CCMenuItemLabel itemWithLabel:resumeLabel block:^(id sender) {
[[GameLayer sharedGameLayer] resumeGame];
}];
// Restart Game button.
CCLabelTTF *restartLabel = [CCLabelTTF labelWithString:#"Restart" fontName:#"Marker Felt" fontSize:32];
restartLabel.color = ccc3(240, 240, 240);
CCMenuItemLabel *restartButton = [CCMenuItemLabel itemWithLabel:restartLabel block:^(id sender) {
[[GameLayer sharedGameLayer] restartGame];
}];
// Main Menu button.
CCLabelTTF *mainMenuLabel = [CCLabelTTF labelWithString:#"Main Menu" fontName:#"Marker Felt" fontSize:32];
mainMenuLabel.color = ccc3(240, 240, 240);
CCMenuItemLabel *mainMenuButton = [CCMenuItemLabel itemWithLabel:mainMenuLabel block:^(id sender) {
[[GameLayer sharedGameLayer] gotoMainMenu];
}];
CCMenu *menu = [CCMenu menuWithItems:resumeButton, restartButton, mainMenuButton, nil];
[menu alignItemsVerticallyWithPadding:10.0];
menu.position = ccp([CCDirector sharedDirector].winSize.width / 2, [CCDirector sharedDirector].winSize.height / 2);
[self addChild:menu];
}
return self;
}
EDIT:
If I add the Pause layer (as a child to GameLayer, and always visible) in the GameLayer's init method, everything works perfectly.. which is a bit weird.
Any input is highly appreciated.
Thanks in advance!
from the line
static GameLayer *sharedGameLayer;
You needed to write this line before #implementation class line to make it isn't class member variable. and at sharedGameLayer method you should change it to
+ (GameLayer *)sharedGameLayer {
#synchronized(self) {
if (sharedGameLayer == nil) {
sharedGameLayer = [[self alloc] init];
}
}
return sharedGameLayer;
}
And, when you define game layer class you don't call init method but you should call sharedGameLayer to make game layer class be singleton.
I would not suggest using singleton pattern on CCLayers, however did you override / edit the CCScene wrapper so it doesn't call init.
For instance
+(id)scene
{
CCScene* scene = [CCScene node];
[scene addChild:[GameLayer sharedGameLayer]];
return scene;
}
Then when you call
[[CCDirector sharedDirector] replaceScene:[GameLayer scene]];
Remember to restart your game right after
[[GameLayer sharedInstance] restart];
Or you could do that in the scene wrapper method.
Hope I got your point.
Atleast this will save you from reallocating space for the game layer all the time hehe..
The culprit of the problem was calling the CCDirector's ReplaceScene: method after pausing the game using the CCDirector's Pause method. What I did was implementing my own "Pause" functionality mainly using: [self pauseSchedulerAndActions]; and also in my case: [[self.spidersBatchNode children] makeObjectsPerformSelector:#selector(pauseSchedulerAndActions)]; for all the children inside of the spriteBatchNode. Another problem arised for which I'll make another question.Thanks for all the replies.
Hi there when my game reaches it's Game Over Scene i have a button to return to the Main menu and for some reason this has stopped working
Here is the code that leads to my Game Over Scene:
[[CCDirector sharedDirector] pause];
[[CCDirector sharedDirector] replaceScene:[ResultsScreen node]];
In my Init function for Game Over
....
mainMenu = [CCMenuItemImage itemFromNormalImage:#"MainMenu.png" selectedImage:#"MainMenuClick.png" target:self selector:#selector(Menu:)];
[mainMenu setAnchorPoint:ccp(0.0f, 0.0f)];
menu = [[CCMenu menuWithItems:mainMenu, nil] retain];
menu.isTouchEnabled = YES;
[menu setPosition:ccp(75, 80)];
[menu setAnchorPoint:ccp(0, 0)];
[self addChild:menu z:Z_INTERACTION];
....
My Selector Function for the button
- (void) Menu:(id)sender
{
NSLog(#"Exit to MainMenu");
[[CCDirector sharedDirector] replaceScene:[MainMenu node]];
}
Now I've compared this with my other code, all my other buttons are working, the only difference is that is that i am specifying a 'z' value.
Any help would be very much appreciated
Update:
Ran a quick check with Leaks and Allocations and nothing seems to be an issue
I found my Issue in my - (void) onEnter {}
I wasn't calling [super onEnter];
I used he code below to handle pause and resume buttons in my game
To Pause:
-(void)pauseTapped{
...
[[SimpleAudioEngine sharedEngine] pauseBackgroundMusic];
[[CCDirector sharedDirector] pause];
...
}
To Resume:
-(void)resumeGame: (id)sender {
...
[self removeChild:pauseMenu cleanup:YES];
[[SimpleAudioEngine sharedEngine] resumeBackgroundMusic];
[[CCDirector sharedDirector] resume];
...
}
The problem is if the used click pause then enterBackground (click Home button) mode
when he returns, the game automatically resume and the pause menu still exist
Any idea will be great
UPDATE:
the AppDelegate code:
- (void)applicationWillResignActive:(UIApplication *)application {
[[CCDirector sharedDirector] pause];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[CCDirector sharedDirector] resume];
}
- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application {
[[CCDirector sharedDirector] purgeCachedData];
}
-(void) applicationDidEnterBackground:(UIApplication*)application {
[[CCDirector sharedDirector] stopAnimation];
}
-(void) applicationWillEnterForeground:(UIApplication*)application {
[[CCDirector sharedDirector] startAnimation];
}
- (void)applicationWillTerminate:(UIApplication *)application {
CCDirector *director = [CCDirector sharedDirector];
[[director openGLView] removeFromSuperview];
[viewController release];
[window release];
[director end];
}
- (void)applicationSignificantTimeChange:(UIApplication *)application {
[[CCDirector sharedDirector] setNextDeltaTimeZero:YES];
}
- (void)dealloc {
[[CCDirector sharedDirector] end];
[window release];
[super dealloc];
}
Thank you
You should add a property to your app delegate that will keep track if pause was caused by user tapping pause button or automatically.
Inside YourAppDelegate.h:
#property (nonatomic) BOOL userPaused;
And inside YourAppDelegate.h:
#synthesize userPaused;
Then inside your scene's pause method, add:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.userPaused = YES;
And inside your scene's resume method, add:
YourAppDelegate *appDelegate = (YourAppDelegate *)[[UIApplication sharedApplication] delegate];
appDelegate.userPaused = NO;
Now you can edit your app delegate's -applicationWillResignActive: method to only resume if userPaused is not set to YES.
- (void)applicationDidBecomeActive:(UIApplication *)application {
if (!self.userPaused) {
[[CCDirector sharedDirector] resume];
}
}