Cocos2d iPhone Game Remove menu from scene - iphone

I have written a few games using Cocos2d iPhone. In all of my previous games I would change scenes when I setup a CCMenu and then leave that scene when I was finished. In my current project, I need the menu to exist in my current scene and be able to open and then close the menu many times. For some reason, I can't seem to understand, removeChild will not remove the menu. I have seen several examples online that show using removeChild, but it does not work for me. Below is my menu code, when the Start/CreateNewAccount button is pressed I want the current menu to be removed from the scene completely.
This is in my init method.
CCMenuItemImage *Start = [CCMenuItemImage itemFromNormalImage:#"MakeLemonade.png" selectedImage:#"MakeLemonadePressed.png"
target:self
selector:#selector(CreateNewAccount:)];
CCMenuItemImage *About = [CCMenuItemImage itemFromNormalImage:#"About.png" selectedImage:#"AboutPressed.png"
target:self
selector:#selector(About:)];
Start.position = ccp(-175, -90);
About.position = ccp(175, -90);
CCMenu *MainMenu = [CCMenu menuWithItems: Start, About, nil];
[Start runAction:[CCFadeIn actionWithDuration:1.0]];
[About runAction:[CCFadeIn actionWithDuration:1.0]];
[self addChild:MainMenu z:6];
}
return self;
}
-(void) BeginMenuLayer {
//this is not working
[self removeChild:MainMenu cleanup:YES];
}

In your init method you've declared MainMenu as a local variable. You're not setting it as a property, so you don't have a reference when you go to remove it later.
1) Make sure you have a property declared for it like this:
#property (nonatomic, retain) CCMenu *MainMenu;
2) Synthesize it at the top of your implementation:
#synthesize MainMenu;
3) Make sure you release it in your dealloc:
-(void)dealloc {
self.MainMenu = nil;
[super dealloc];
}
4) When you construct it, assign it to your property rather than a local variable:
self.MainMenu = [CCMenu menuWithItems: Start, About, nil];
Now you have a retained reference to the object, which you can pass later to removeChild:cleanup:.

Related

how do I add a menu to an already-made game in cocos2d and box2d for iphone?

I have an existing game that I want to have a title screen with a button "play" to the app that loads the game.
EDIT: The interface was made in Level Helper
Here's how you can implement a menu that changes scenes with a transition. In your HelloWorldLayer.m file, add this:
-(id) init
{
if( (self=[super init])) {
CCMenuItemImage *menuImage = [CCMenuItemImage itemFromNormalImage:#"yourimage.png" selectedImage:#"Icon.png" target:self selector:#selector(changeScene:)];
CCMenu *menu;
menu = [CCMenu menuWithItems:menuImage, nil];
[self addChild:menu];
}
return self;
}
-(void) changeScene:(id)sender
{
[[CCDirector sharedDirector] replaceScene:[CCTransitionZoomFlipX transitionWithDuration:1 scene:[Scene1 node]]];
}
This creates a menu item image assigned to a selector, adds it to a menu, and then on click, transitions to a new scene, which I will show you how to do now. Create a new class called Scene1, and just to show that the transition worked, we will add a sprite in this new scene. In your init method:
-(id) init
{
if( (self=[super init])) {
sprite = [CCSprite spriteWithFile:#"yourimage.png"];
sprite.position = ccp(100,200);
[self addChild:sprite];
}
return self;
}
If you see this new sprite on the screen, it means that everything worked.
I'll assume by saying u have an existing game, that u have the source code of the game.
All u need is to make a new CCLayer with a CCMenu (containing the CCMenuButton u want) that all it does is load the CCLayer that loads as u start the game, and make ur CCLayer the one that gets loaded as u start the app.
create a menu using the following code:
// Intalize your menu item
CCMenuItem *menuItem = [CCMenuItemFont itemFromString:#"This is what you want your item to say" target:self selector:#selector(selectorToHandleYourSelection)];
// Define where you want your item to be
menuItem.position = ccp(100,100);
// Intalize a menu for your menu item
CCMenu *menu = [CCMenu menuWithItems:menuItem, nil];
// Add the 'menu' as a child to your layer
[self addChild:menu];
// If the item position isn't defined then you can align the items horizontally
[menu alignItemsHorizontally];

Cocos2d - CCMenu with multiple buttons calls wrong selector on first load

Here's an interesting dilemma. I have two CCMenus being loaded on a page, each with two CCMenuItemImages as buttons. All four buttons call the same function, which decides what to do using a switch statement that goes off the caller's tag value.
The four buttons are start, tutorial, options, and credits. I have them split into two menus so that I can horizontally and vertically align them in a faux grid. This layer is the main menu layer, so it is the first thing to load after the game starts.
The problem is that when the game first loads, pressing any button will call the "options" button. Not just the function, pressing any button on the menu activates the options button's selected state. If I press "start," for instance, the start button's selected state (a glow around the image) doesn't work–the options button glows instead.
Once I get into the options menu, and then back out of it, the main menu works as expected, with each button activating its requisite function.
I should note that I've also run a clean, and removed the app from both the simulator and my iPhone and rebuilt it.
Here's my .h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Constants.h"
#import "GameManager.h"
#interface MainMenuLayer : CCLayer {
CCMenu *mainMenuTop;
CCMenu *mainMenuBottom;
}
#end
And this is my .m:
#import "MainMenuLayer.h"
// Private methods
#interface MainMenuLayer()
- (void)displayMainMenu;
#end
#implementation MainMenuLayer
- (void)playScene:(CCMenuItemFont*) itemPassedIn {
if ([itemPassedIn tag] == 1) {
CCLOG(#"Tag 1 found, Scene 1");
[[GameManager sharedGameManager] runSceneWithID:kGameplayScene];
} else if ([itemPassedIn tag] == 2) {
CCLOG(#"Tag was: %d", [itemPassedIn tag]);
CCLOG(#"Placeholder for next chapters");
} else if ([itemPassedIn tag] == 3) {
CCLOG(#"Tag 3, Options");
[[GameManager sharedGameManager] runSceneWithID:kOptionsScene];
} else if ([itemPassedIn tag] == 4) {
CCLOG(#"Tag 4, Credits");
[[GameManager sharedGameManager] runSceneWithID:kCreditsScene];
}
}
- (void)displayMainMenu {
CGSize winSize = [CCDirector sharedDirector].winSize;
// Main Menu Top Layer Buttons
CCMenuItemImage *playGameButton = [CCMenuItemImage itemFromNormalImage:#"button-start-up.png" selectedImage:#"button-start-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[playGameButton setTag:1];
CCMenuItemImage *tutorialButton = [CCMenuItemImage itemFromNormalImage:#"button-tutorial-up.png" selectedImage:#"button-tutorial-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[tutorialButton setTag:2];
// Main Menu Bottom Layer Buttons
CCMenuItemImage *optionsButton = [CCMenuItemImage itemFromNormalImage:#"button-options-up.png" selectedImage:#"button-options-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[optionsButton setTag:3];
CCMenuItemImage *creditsButton = [CCMenuItemImage itemFromNormalImage:#"button-credits-up.png" selectedImage:#"button-credits-down.png" disabledImage:nil target:self selector:#selector(playScene:)];
[creditsButton setTag:4];
mainMenuTop = [CCMenu menuWithItems:playGameButton,tutorialButton,nil];
mainMenuBottom = [CCMenu menuWithItems:optionsButton,creditsButton,nil];
[mainMenuTop alignItemsHorizontallyWithPadding: 10.0f];
[mainMenuTop setPosition: ccp(winSize.width/2, -500)];
[mainMenuBottom alignItemsHorizontallyWithPadding:10.0f];
[mainMenuBottom setPosition:ccp(winSize.width/2, -600)];
id moveActionTop = [CCMoveTo actionWithDuration:0.5f position:ccp(winSize.width/2, 150)];
id moveEffectTop = [CCEaseIn actionWithAction:moveActionTop rate:1.0f];
[mainMenuTop runAction:moveEffectTop];
[self addChild:mainMenuTop z:2 tag:kMainMenuTagValue];
id moveActionBottom = [CCMoveTo actionWithDuration:0.5f position:ccp(winSize.width/2, 75)];
id moveEffectBottom = [CCEaseIn actionWithAction:moveActionBottom rate:1.0f];
[mainMenuBottom runAction:moveEffectBottom];
[self addChild:mainMenuBottom z:3 tag:kMainMenuBottomTagValue];
}
-(id)init {
self = [super init];
if (self != nil) {
[self displayMainMenu];
}
return self;
}
#end
I ended up just positioning things manually. I tried to use the method linked by LearnCocos2D, but I couldn't determine how to make it work properly; the items didn't end up in a proper grid.

CCMenu doesnt work in iPad

I'm creating an universal application and my CCMenu appears just fine on both iPhone, iPhone 4 and iPad. However the buttons do nothing when touched on the iPad.
I have no specific iPad code other than modifying the contentScaling property so that the iPad uses the same images as the iPhone 4. This means that the same images work on iPhone 4 but not on the iPad.
I am using cocos2d 0.99.rc0 and the iOS 4.1 SDK. I don't even know where to start troubleshooting this.
The only oddity i noticed recently is that the iPad seems to draw the menu scene once, then quickly redraws it for some reason, moving everything one pixel or something. My menu class is very simple and has no "refreshing" code or anything that is supposed to move. This doesnt happen on either low or high res iPhones.
Here is my code, sloppy but yet very simple.
MainMenu.m:
CCMenuItemImage * playItem = [self makeMenuButtonWithSprite:#"Play.png" withSelector:#selector(play:)];
CCMenuItemImage * resumeItem = [self makeMenuButtonWithSprite:#"Resume.png" withSelector:#selector(resume:)];
CCMenuItemImage * optionsItem = [self makeMenuButtonWithSprite:#"Options.png" withSelector:#selector(options:)];
CCMenuItemImage * helpItem = [self makeMenuButtonWithSprite:#"Help.png" withSelector:#selector(help:)];
CCMenu *myMenu;
// Check if there is a valid savegame by comparing versions.
if ([[uD stringForKey:#"CFBundleVersion"] isEqualToString:[[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleVersion"]] ) {
myMenu = [CCMenu menuWithItems:playItem, resumeItem, optionsItem, helpItem, nil];
} else {
myMenu = [CCMenu menuWithItems:playItem, optionsItem, helpItem, nil];
}
// Arrange the menu items vertically
[myMenu alignItemsVerticallyWithPadding:0.0f];
myMenu.position = ccp(dB.wWidth/2,dB.wHeight/2);
// add the menu to your scene
[self addChild:myMenu z:100];
And the CCMenuItemImage factory:
- (CCMenuItemImage *)makeMenuButtonWithSprite:(NSString *)spriteFileName withSelector:(SEL)selector {
CCSprite *spriteForButton = [CCSprite spriteWithFile:spriteFileName];
spriteForButton.anchorPoint = ccp(0.5f,0.5f);
CCMenuItemImage * buttonImage =[CCMenuItemImage itemFromNormalImage:#"button.png"
selectedImage: #"button.png"
target:self
selector:selector];
[buttonImage addChild:spriteForButton z:100];
spriteForButton.position = ccp([buttonImage boundingBox].size.width/2,([buttonImage boundingBox].size.height/2)-5);
return buttonImage;
}
I don't think that any known bug exists for this issue. Not sure how to debug this without seeing any code, but, if it helps, here's some code of mine that successfully adds a menu using cocos2d 0.99.5, on iOS 4.0, 4.1 and 4.2 (no difference when I upgraded):
-(void) initBottomMenu {
CCMenuItem *aboutButton = [self gameButtonWithName:#"about" selector:#selector(onAbout:)];
CCMenuItem *settingsButton = [self gameButtonWithName:#"settings" selector:#selector(onSettings:)];
CCMenuItem *tutButton = [self gameButtonWithName:#"tutorial" selector:#selector(onTutorial:)];
CCMenu *menu = [CCMenu menuWithItems:aboutButton, settingsButton, tutButton, nil];
menu.position = ccp(xPos, yPos);
[menu alignItemsHorizontallyWithPadding:45.0];
[self addChild:menu];
}
The gameButtonWithName:selector: method looks like this:
-(CCMenuItem *) gameButtonWithName:(NSString *)name selector:(SEL)s {
NSString *iPadSuffix = #"IPad";
NSString *normal = [[NSString alloc] initWithFormat:#"%#Btn%#.png", name, iPadSuffix, nil] ;
NSString *selected = [[NSString alloc] initWithFormat:#"%#Btn%#.png", name, iPadSuffix, nil];
CCMenuItem *retButton = [CCMenuItemImage itemFromNormalImage:normal
selectedImage:selected
disabledImage:selected
target:self
selector:s];
[selected release];
[normal release];
return retButton;
}
sort of sloppy, but it works out well for adding a menu to my main scene.
Problem found. This was related to my custom hack to make the iPad load retina graphics. The problem was in my appDelegate where i set the contentScaleFactor which made ccDirector scale and UIScreen scale mismatch.
Problem boiled down to graphics being big but cocos2d thinking that coordinates are low res.

scaling a CCMenuItem in Cocos2d (Objective-C)

I'm trying to make a CCMenuItem that has scaled images. For example, I tried:
CCSprite* normalSprite = [CCSprite spriteWithFile:#"button_play.png"];
CCSprite* selectedSprite = [CCSprite spriteWithFile:#"button_play.png"];
selectedSprite.scale = 1.2;
CCMenuItem menuItem = [CCMenuItemSprite
itemFromNormalSprite:normalSprite
selectedSprite:selectedSprite
target:self
selector:#selector(onPlay:)];
But it looks like CCMenuItemSprite ignores the scale of the underlying sprites. Is there a way to do this (aside from just creating scaled versions of the underlying images)? Thanks.
Thyrgle is correct about how CCMenuItem works.
However, there certainly is a way to do what you want. All you need to do is subclass CCMenuItem and override the selected and unselected methods to achieve what you want. In fact, I'm pretty sure you could just cut and paste the code from CCMenuItemLabel, because scaling the item to 1.2 is exactly what it does. (In fact, it does it better, since it animates the scale change.)
-(void) selected
{
// subclass to change the default action
if(isEnabled_) {
[super selected];
[self stopActionByTag:kZoomActionTag];
CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:1.2f];
zoomAction.tag = kZoomActionTag;
[self runAction:zoomAction];
}
}
-(void) unselected
{
// subclass to change the default action
if(isEnabled_) {
[super unselected];
[self stopActionByTag:kZoomActionTag];
CCAction *zoomAction = [CCScaleTo actionWithDuration:0.1f scale:1.0f];
zoomAction.tag = kZoomActionTag;
[self runAction:zoomAction];
}
}
CCMenuItemImage Class is also available for displaying image with its scale in CCMenu.Please Check this link http://www.cocos2d-iphone.org/forum/topic/8310
[mainMenu alignItemsVerticallyWithPadding:15.0f];
No there is not another way. The thing is that menuItem is only acknowledging the files part of the sprites. It is not looking at other properties such as the scale property.

Few questions on iphone memory management and cocos2d

I'm working on my first app and have a few questions on memory management.
First Question:
I'm working on an intro scene that looks like this
#import "Intro_Scene.h"
#import "Main_Menu.h"
#import "Label.h"
#implementation Intro_Scene
#synthesize logo,label;
-(id) init
{
self = [super init];
if(self != nil)
{
//Load logo image and set position
logo = [Sprite spriteWithFile:#"AVlogo_1.png"];
logo.position = ccp(-50, 0);
logo.scale = 1.8f;
[self addChild: logo];
//Creates 3 actions for the logo sprite
id action0 = [MoveTo actionWithDuration:0 position:ccp(160,270)];
id action1 = [FadeIn actionWithDuration:3];
id action2 = [FadeOut actionWithDuration:3];
//Logo runs the actions
[logo runAction: [Sequence actions:action0,action1, action2, nil]];
//Schedules the changeScene method to switch scenes to main menu within 6 seconds of loading.
[self schedule: #selector(changeScene) interval:6.0f];
//Creates a label and positions it, Alternative Visuals
label = [Label labelWithString:#"Alternative Visuals" fontName:#"Verdana" fontSize:22];
label.position = ccp(160, 120);
[self addChild:label];
}
return self;
}
//Method called after intro has run its actions, after 6 seconds it switches scenes.
-(void)changeScene
{
[self removeChild:logo cleanup:YES];
[self removeChild:label cleanup:YES];
Main_Menu *mainMenu = [Main_Menu node];
[[Director sharedDirector] replaceScene: mainMenu];
}
-(void)dealloc
{
[[TextureMgr sharedTextureMgr] removeUnusedTextures];
[label release];
[logo release];
[super dealloc];
}
#end
Have I released everything correctly, and avoided leaks? I ran it in instruments multiple times and it found no leaks and used about 2mb of memory, is that to much or the amount to be expected? Also does the dealloc method get called when the scene is replaced?
Question 2:
My main menu is set up like this
#import "Main_Menu.h"
#import "Sprite.h"
#import "cocos2d.h"
#implementation Main_Menu
#synthesize background, controlLayer;
-(id) init
{
self = [super init];
if(self != nil)
{
//Create the default background for main menu not including directional pad and highlight box
background = [Sprite spriteWithFile:#"Main_Menu_bg.png"];
background.position = ccp(160,240);
[self addChild:background];
//Adds the control later class to the main menu, control layer class displays and controls the directional pad and selector.
ControlLayer *layer = [[ControlLayer alloc] init];
self.controlLayer = layer;
[layer release];
[self addChild: controlLayer];
}
return self;
}
-(void) dealloc
{
[seld removeChild:background cleanup:YES];
[[TextureMgr sharedTextureMgr] removeUnusedTextures];
[background release];
[controlLayer release];
[super dealloc];
}
#end
Once again am I doing everything correctly? The layer ControlLayer I'm adding to this scene contains a directional pad sprite that the user uses to navigate the menu. In instruments It also confirms that their is no memory leaks, and it uses 4.79 mb of memory. Once again is that a reasonable amount? I will most likely switch to using AtlasSprite and AtlastSpriteManager to conserve memory.
I'm new to cocos2d, so if you see I'm doing anything wrong point it out! I'd rather fix bad habits in the early stages. And if you have any future tips for memory management please share.
Don't release logo, label, or background. You didn't alloc/copy/new/retain them, so you don't own them and must not release them.
I assume the controllerLayer property has the retain attribute? If not, you probably mean to do so.
In general I'd suggest two things going forward.
Read and understand the Cocoa Memory Management Fundamentals
Run the Clang analyzer on your code. This is available in Xcode 3.2 via Build->Build and Analyze. It will help detect these memory issues.
Also check out this SO question.