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.
Related
I'm facing a performance issue whenever the code reach "replaceScene". It happens only in the Play Scene. So after the game is over, I display a score, and then, it's time for CCDirector to do replaceScene in order to go back to the main menu.
After waiting for around 20 seconds then it finally display the main menu. But somehow this is not right, player will feel that the game suddenly hang. Then I tried to put some animation like preloader, it happens the same, the preloader picture did animate a while then suddenly stop, and I think due to the same issue triggered by replaceScene, although still it'll display the main menu scene. Care to give some tips how to speed up the releasing of all the objects which no longer needed.
Hoping to get a solution from experts here. Thanks.
Here is my code :
............
//button at the score pop up sprite
CCMenuItem *btContinue = [CCMenuItemImage itemFromNormalImage:BTCONTINUE
selectedImage:BTCONTINUE_ON
target:self
selector:#selector(goLoader)];
btContinue.anchorPoint = ccp(0,0);
btContinue.position = ccp(340, 40);
CCMenu *menu = [CCMenu menuWithItems:btContinue, nil];
menu.position = CGPointZero;
[self addChild:menu z:ZPOPUP_CONTENT];
//prepare the loader, but set visible to NO first
CCSprite *loaderBg = [CCSprite spriteWithFile:LOADER_FINISH];
loaderBg.anchorPoint = ccp(0,0);
loaderBg.position = ccp(0,0);
loaderBg.visible = NO;
[self addChild:loaderBg z:ZLOADER_BG tag:TAG_LOADER_BG];
NSLog(#"prepare loader finish");
//animate loader
CCSprite *loaderPic = [[CCSprite alloc] initWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:LOADER]];
loaderPic.anchorPoint = ccp(0.5,0.5);
loaderPic.position = ccp(200,35);
loaderPic.visible = NO;
[self addChild:loaderPic z:ZLOADER_PIC tag:TAG_LOADER_PIC];
[loaderPic runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:0.05f angle:10.0f]]];
}
-(void)goLoader {
NSLog(#"goMainMenuScene");
CCSprite *tmpBg = (CCSprite *) [self getChildByTag:TAG_LOADER_BG];
if (tmpBg != nil)
tmpBg.visible = YES;
CCSprite *tmpPic = (CCSprite *) [self getChildByTag:TAG_LOADER_PIC];
if (tmpPic != nil)
tmpPic.visible = YES;
double time = 2.0;
id delay = [CCDelayTime actionWithDuration: time];
id proceed = [CCCallFunc actionWithTarget:self selector:#selector(goMainMenuScene)];
id seq = [CCSequence actions: delay, proceed, nil];
[self runAction:seq];
}
-(void)goMainMenuScene {
[[GameManager sharedGameManager] runSceneWithID:SCENE_MAIN_MENU];
}
Your problem is most likely the new scene, and whatever happens in the new scene's and its child node's init methods. Loading resource files can take quite a while. If you defer doing this into onEnter you might see better results. But 20 seconds, that's a lot. Check what you're doing that takes THIS long. I bet it's loading a gross amount of resources, or loading them in an extremely inefficient way. JPG files are known to load very slowly, if you use JPG convert them to PNG.
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];
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:.
am having game with 10 levels. i want to change the second level lock image to unlock when first level is completed.
am using 20 images ( 10 locked and 10 unlocked).
am using cc menus to display the number images.
for example(code):-
CCMenuItemImage *startButton12 = [CCMenuItemImage itemFromNormalImage:#"ten_new-lock.png"
selectedImage:#"ten_new-lock.png" target:self
selector:#selector(ten:)];
menu1 = [CCMenu menuWithItems: startButton3,startButton4,startButton5,startButton6,startButton7,startButton8,startButton9,startButton10,startButton11,startButton12, nil];
menu1.position = ccp(240,30);
[menu1 alignItemsHorizontally];
[menuLayer1 addChild: menu1];
am using below code for remember the level completed.
int lastLevelCompleted= [[NSUserDefaults standardUserDefaults] integerForKey:#"levelCompleted"];
if(currentLevel >lastLevelCompleted){
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:currentLevel forKey:#"levelCompleted"];
**now, how to change the lock to unlock images.
( if am doing here wrong)there is other way to solve means provide that. i have to implement that one.**
You could set a disabledImage when you create each CCMenuItemImage:
// create items by delclaring also a "disabled" image
CCMenuItemImage *menuItem = [CCMenuItemImage itemFromNormalImage:normalImage
selectedImage:selectedImage
disabledImage:disabledImage
target:self
selector:#selector(callbackMethod)];
Then just switch the state of the button as requested:
// then just use setIsEnabled to switch the state
[menuItem setIsEnabled:NO];
Cocos swaps the images for you.
I have the same idea in one of my games. I solved it with separate lock and done icons that I have positioned on top of each menu item that represents a level.
Just create your menu items normally. Don't try to represent locked or done states with the menu item's icon. Instead create a smaller locked and done icons that you will instantiate as sprites and position on top of each menu item.
Here is the relevant part of my menu layer's init method (I'm using a sprite atlas to store all my images):
// I save the state of each level as a character in a NSMutableString:
self.completedState = 0x0043; // "C" (Completed)
self.openState = 0x004f; // "O"
self.lockedState = 0x004c; // "L"
self.dungeonAvailabilityState = #"COLLLLLLLLLLLLLLLL"; // in reality I get this string from a global object
// calc the position for the dungeon icon at row, column
x = (column*56)+148;
y = 244-(row*56);
// get the dungeon state
stateIndex = (row*columns)+column;
dungeonState = [self.dungeonAvailabilityState characterAtIndex:stateIndex];
// calc the position of the badges using offset from the menu item's icon
lockedX = x - DungeonsScreen_BadgeXoffset;
lockedY = y - DungeonsScreen_BadgeYoffset;
doneX = x - DungeonsScreen_BadgeXoffset;
doneY = y + DungeonsScreen_BadgeYoffset;
if (dungeonState == self.lockedState) {
// add the lock icon
[super badgeIconFromFrame:#"icon_lock.png" xPos:lockedX yPos:lockedY spriteTag:t++];
}
and my helper method badgeIconFromFrame looks like this:
- (void) badgeIconFromFrame:(NSString*)spriteName xPos:(float)x yPos:(float)y spriteTag:(int)t {
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
CCSprite* badgeSprite = [CCSprite spriteWithSpriteFrameName:spriteName];
badgeSprite.position = CGPointMake(x, y);
[self addChild:badgeSprite z:zIndexDecoration tag:t];
}
What about (void) - setIsEnabled: method of CCMenuItem ?
Not sure if it works:
if(unlock){
[menuLayer1 removeChild: menu1 cleanup:YES];
CCMenuItemImage *startButton12 = [CCMenuItemImage itemFromNormalImage:#"ten_new-unlock.png"
selectedImage:#"ten_new-unlock.png" target:self
selector:#selector(ten:)];
menu1 = [CCMenu menuWithItems: startButton3,startButton4,startButton5,startButton6,startButton7,startButton8,startButton9,startButton10,startButton11,startButton12, nil];
menu1.position = ccp(240,30);
[menu1 alignItemsHorizontally];
[menuLayer1 addChild: menu1];
}
else
{
CCMenuItemImage *startButton12 = [CCMenuItemImage itemFromNormalImage:#"ten_new-lock.png"
selectedImage:#"ten_new-lock.png" target:self
selector:#selector(ten:)];
menu1 = [CCMenu menuWithItems: startButton3,startButton4,startButton5,startButton6,startButton7,startButton8,startButton9,startButton10,startButton11,startButton12, nil];
menu1.position = ccp(240,30);
[menu1 alignItemsHorizontally];
[menuLayer1 addChild: menu1];
}
did u try using db (sqlite) to store the value of locked or unlocked.
It will definitely work.
But it is a big process.
Just use [menuItem setNormalImage:lockedButton];
LockedButton being another CCMenuItemImage with a new file.
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.