Iam trying to create simple menu according to the book CCMenuItem . here is my MenuView.m code
bot nothing happens just a black screen appears ! without any menu !
iam working with COCOS2D 0.99.5
//
// MenuView.m
// Sence
#import "MenuView.h"
#import "HelloWorldScene.h"
#implementation MenuView
// in dastor hatman bayad dar menu badi neveshte shavad ke be onvane ye view shenasande shavad
+(id) scene
{
CCScene* scene = [CCScene node];
CCLayer* layer = [MenuView node];
[scene addChild:layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
CCLOG(#"init %#", self);
// wait a short moment before creating the menu so we can see it scroll in
[self schedule:#selector(createMenu:) interval:2];
}
return self;
}
- (void) menuItem1Touched {
NSLog(#"test");
}
- (void) createMenu:(ccTime) delta {
[self unschedule:_cmd];
CGSize size = [[CCDirector sharedDirector] winSize];
// set CCMenuItemFont default properties
[CCMenuItemFont setFontName:#"Helvetica-BoldOblique"];
[CCMenuItemFont setFontSize:40];
// create a few labels with text and selector
CCMenuItemFont* item1 = [CCMenuItemFont itemFromString:#"Go Back!" target:self selector:#selector(menuItem1Touched:)];
// create the menu using the items
CCMenu* menu = [CCMenu menuWithItems:item1, nil];
menu.position = CGPointMake(-(size.width / 2), size.height / 2);
menu.tag = 100;
[self addChild:menu];
// calling one of the align methods is important, otherwise all labels will occupy the same location
[menu alignItemsVerticallyWithPadding:40];
}
#end
Your this line:
CCMenuItemFont* item1 = [CCMenuItemFont itemFromString:#"Go Back!" target:self selector:#selector(menuItem1Touched:)];
should be
CCMenuItemFont* item1 = [CCMenuItemFont itemFromString:#"Go Back!" target:self selector:#selector(menuItem1Touched)];
Change from menuItem1Touched: to menuItem1Touched.. Remove the :
Tested and works..
Related
I am making a 14 year old kid making a game in cocos2d. I am pretty new to cocos2d. I want to display the same coin sprite next to each other to make a pattern.
So I added this in my main gameplay layer:
- (void)coinPatterns {
menu = [CCMenu menuWithItems:[Coins class], [Coins class], self, nil];
[menu alignItemsHorizontally];
[self addChild:menu];
}
And this is how I am initializing menu:
[[GameMechanics sharedGameMechanics] setSpawnRate:50 forMonsterType:menu];
This is what is in my coins class:
- (id)initWithMonsterPicture
{
self = [super initWithFile:#"coin.png"];
if (self)
{
CGRect screenRect = [[CCDirector sharedDirector] screenRect];
CGSize spriteSize = [self contentSize];
posX = screenRect.size.width + spriteSize.width * 0.5f;
posY = 150;
self.initialHitPoints = 1;
self.animationFrames = [NSMutableArray array];
[self scheduleUpdate];
inAppCurrencyDisplayNode.score = [Store availableAmountInAppCurrency];
}
coinValue = 3;
return self;
}
- (void)spawn
{
self.position = CGPointMake(posX, posY);
self.visible = YES;
}
- (void)gotCollected {
self.visible = FALSE;
self.position = ccp(-MAX_INT, 0);
[Store addInAppCurrency:coinValue];
}
I keep getting a Incompatible pointer types sending 'class' to parameter of type 'CCMenuItem'. Can someone please tell me how I should change the code so that this works?
Thanks!
menuWithItems: takes an array of CCMenuItem objects, you are sending a class itself. I don't know what the class Coin does, but if the purpose is to show an image and then do something when it's tapped I suggest you to do this:
CCMenuItem *myCoin1 = [CCMenuItemImage
itemFromNormalImage:#"coin.png" selectedImage:#"coinSelected.png"
target:self selector:#selector(coin1WasTapped:)];
CCMenuItem *myCoin2 ...
menu = [CCMenu menuWithItems: myCoin1, myCoin2, myCoin3, ..., nil];
You should create a method coin1WasTapped: that will be called when the coin was tapped, you can "collect" the coins here. Maybe remove them from the menu or an animation.
If you are going to create many coins I suggest you to use a for loop to create them all in an array. This way it will be easier to manipulate later.
This tutorial is really good, it can help you to understand better what you need to do and how to do it.
Good luck!
sorry if the question is too dull but I have been trying to understand as much as possible from Itterheim's book book's code example and can't figure out how this works.
In my "HelloWorldLayer.m" I create an instance of a class named "MusicLayer" that extends CCLayer The inspiration of this was that in the example named "ShootEmUp" (Chapter 8 of 1) there is an InputLayer. I thus thought that I would create a MusicLayer to deal with the various music files I have in my game and got some problems (will explain at the end of the post). I got most of the music related code code from RecipeCollection02, Ch6_FadingSoundsAndMusic of the Cocos2d Cookbook .
But first I would like to introduce some of my background thoughts that might have lead to this problem with the hope to have some clear reference on this.
I am used to a "model view controller" (MVC wikipedia link) approach as I come from a C++ and Java background and never coded a game before and it does seem to me that the gaming paradigms are a bit different from "the classic" Software Engineering University approach (don't take me wrong, I am sure that in many Universities they don't teach this stuff anymore :)).
The point is that it seems to me that the "classic" Software Engineering approach is lost in those examples (for instance in the ShootEmUp code the instance of InputLayer has a reference of the class GameScene -see below- and at the same time in InputLayer there is a reference to the static shared instance of GameScene -see below-).
//From GameScene
+(id) scene
{
CCScene* scene = [CCScene node];
GameScene* layer = [GameScene node];
[scene addChild:layer z:0 tag:GameSceneLayerTagGame];
InputLayer* inputLayer = [InputLayer node];
[scene addChild:inputLayer z:1 tag:GameSceneLayerTagInput];
return scene;
}
//From InputLayer
-(void) update:(ccTime)delta
{
totalTime += delta;
// Continuous fire
if (fireButton.active && totalTime > nextShotTime)
{
nextShotTime = totalTime + 0.5f;
GameScene* game = [GameScene sharedGameScene];
ShipEntity* ship = [game defaultShip];
//Code missing
}
}
I was told that static instances are things to avoid. Despite this I understand the benefit of having a GameScene staic instance but I still get confused on the biderectional reference (from GameScene to InputLayer and viceversa)..:
Is this common practice in Game programming?
Is there a way to avoid this approach?
I will now paste my code and I admit it, is probably rubbish and there will be probably some obvious error as I am trying to do a very simple thing and I do not manage.
So here we are. That's the MusicLayer class (it contains commented out code as I had previously tried to make it a shared class instance):
//
// MusicLayer.h
// ShootEmUp
//
// Copyright 2012 __MyCompanyName__. All rights reserved.
//
//Enumeration files should be the way those are referred from outside the class..
enum music_files {
PLAYER_STANDARD = 1,
};
enum sound_files{
A = 0,
B = 1,
C = 2,
ABC = 4,
AAC = 5,
ACA = 6,
//And so on and so forth..
};
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CDXPropertyModifierAction.h"
#interface MusicLayer : CCLayer {
SimpleAudioEngine *sae;
NSMutableDictionary *soundSources;
NSMutableDictionary *musicSources;
}
//In this way you can load once the common sounds on the sharedMusicLayer, have the benefit of keep tracks playing on scene switching as well as being able to load tracks and sounds before each level
//+(MusicLayer*) sharedMusicLayer;
/** API **/
//PLAY - sound undtested
-(void) playBackgroundMusic:(enum music_files) file;
-(void) playSoundFile:(enum sound_files) file;
-(void) loadPopLevel;
/** Private methods **/
//Utilities methods
-(NSString *) _NSStringFromMusicFiles: (enum music_files) file;
-(NSString *) _NSStringFromSoundFiles: (enum sound_files) file;
//LOAD - Meant to be private methods
-(CDLongAudioSource*) _loadMusic:(NSString*)fn;
-(CDSoundSource*) _loadSoundEffect:(NSString*)fn;
//FADE - sound undtested
-(void) _fadeOutPlayingMusic;
-(void) _fadeInMusicFile:(NSString*)fn;
-(void) _playSoundFile:(NSString*)fn;
#end
And here we are with the .m file:
#import "MusicLayer.h"
#implementation MusicLayer
/**
static MusicLayer* instanceOfMusicLayer;
+(MusicLayer*) sharedMusicLayer
{
NSAssert(instanceOfMusicLayer != nil, #"MusicLayer instance not yet initialized!");
return instanceOfMusicLayer;
}**/
-(id) init
{
CCLOG(#"In Init");
if ((self = [super init]))
{
CCLOG(#"Inside Init");
//instanceOfMusicLayer = self;
//Initialize the audio engine
sae = [SimpleAudioEngine sharedEngine];
//Background music is stopped on resign and resumed on become active
[[CDAudioManager sharedManager] setResignBehavior:kAMRBStopPlay autoHandle:YES];
//Initialize source container
soundSources = [[NSMutableDictionary alloc] init];
musicSources = [[NSMutableDictionary alloc] init];
}
return self;
}
-(void) loadPopLevel{
CCLOG(#"In loadPopLevel");
[self _loadSoundEffect:[self _NSStringFromSoundFiles:A]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:B]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:C]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:ACA]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:ABC]];
[self _loadSoundEffect:[self _NSStringFromSoundFiles:AAC]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
[self _loadMusic:[self _NSStringFromMusicFiles:PLAYER_STANDARD]];
}
/** UTILITIES METHODS **/
//This function is key to define which files we are going to load..
-(NSString *) _NSStringFromMusicFiles: (enum music_files) file{
CCLOG(#"In _NSStringFromMusicFiles");
switch (file) {
case PLAYER_STANDARD:
return #"a.mp3";
default:
NSAssert(0 == 1, #"Invalid argument");
break;
}
}
-(NSString *) _NSStringFromSoundFiles: (enum sound_files) file{
CCLOG(#"In _NSStringFromSoundFiles");
switch (file) {
case A:
return #"shot.caf";
case B:
return #"shot.caf";
case C:
return #"shot.caf";
case AAC:
return #"Wow.caf";
case ABC:
return #"Wow.caf";
case ACA:
return #"Wow.caf";
default:
NSAssert(0 == 1, #"Invalid argument");
break;
}
}
/** PLAY METHODS **/
-(void) _playSoundFile:(NSString*)fn {
CCLOG(#"In _playSoundFile");
//Get sound
CDSoundSource *sound = [soundSources objectForKey:fn];
sound.looping = YES;
//Play sound
if(sound.isPlaying){
[sound stop];
}else{
[sound play];
}
}
-(void) _fadeInMusicFile:(NSString*)fn {
CCLOG(#"In _fadeInMusicFile");
//Stop music if its playing and return
CDLongAudioSource *source = [musicSources objectForKey:fn];
if(source.isPlaying){
[source stop];
return;
}
//Set volume to zero and play
source.volume = 0.0f;
[source play];
//Create fader
CDLongAudioSourceFader* fader = [[CDLongAudioSourceFader alloc] init:source interpolationType:kIT_Linear startVal:source.volume endVal:1.0f];
[fader setStopTargetWhenComplete:NO];
//Create a property modifier action to wrap the fader
CDXPropertyModifierAction* fadeAction = [CDXPropertyModifierAction actionWithDuration:1.5f modifier:fader];
[fader release];//Action will retain
[[CCActionManager sharedManager] addAction:[CCSequence actions:fadeAction, nil] target:source paused:NO];
}
-(void) _fadeOutPlayingMusic {
CCLOG(#"In _fadeOutPlayingMusic");
for(id m in musicSources){
//Release source
CDLongAudioSource *source = [musicSources objectForKey:m];
if(source.isPlaying){
//Create fader
CDLongAudioSourceFader* fader = [[CDLongAudioSourceFader alloc] init:source interpolationType:kIT_Linear startVal:source.volume endVal:0.0f];
[fader setStopTargetWhenComplete:NO];
//Create a property modifier action to wrap the fader
CDXPropertyModifierAction* fadeAction = [CDXPropertyModifierAction actionWithDuration:3.0f modifier:fader];
[fader release];//Action will retain
CCCallFuncN* stopAction = [CCCallFuncN actionWithTarget:source selector:#selector(stop)];
[[CCActionManager sharedManager] addAction:[CCSequence actions:fadeAction, stopAction, nil] target:source paused:NO];
}
}
}
/** LOADING METHODS **/
-(CDLongAudioSource*) _loadMusic:(NSString*)fn {
CCLOG(#"In _loadMusic" );
//Init source
CDLongAudioSource *source = [[CDLongAudioSource alloc] init];
source.backgroundMusic = NO;
[source load:fn];
//Add sound to container
[musicSources setObject:source forKey:fn];
return source;
}
-(CDSoundSource*) _loadSoundEffect:(NSString*)fn {
CCLOG(#"In _loadSoundEffect" );
//Pre-load sound
[sae preloadEffect:fn];
//Init sound
CDSoundSource *sound = [[sae soundSourceForFile:fn] retain];
//Add sound to container
[soundSources setObject:sound forKey:fn];
return sound;
}
/** Public methods **/
//Play music callback
-(void) playBackgroundMusicNumber:(enum music_files) file {
CCLOG(#"In playBackgroundMusic");
switch (file) {
case PLAYER_STANDARD:
[self _fadeOutPlayingMusic];
[self _fadeInMusicFile: [self _NSStringFromMusicFiles:PLAYER_STANDARD]];
break;
default:
break;
}
}
-(void) playSoundFile:(enum sound_files) file {
CCLOG(#"In playSoundFile");
switch (file) {
case A:
[self _playSoundFile:[self _NSStringFromSoundFiles:A]];
break;
case B:
[self _playSoundFile:[self _NSStringFromSoundFiles:B]];
break;
case C:
[self _playSoundFile:[self _NSStringFromSoundFiles:C]];
break;
case AAC:
[self _playSoundFile:[self _NSStringFromSoundFiles:AAC]];
break;
case ABC:
[self _playSoundFile:[self _NSStringFromSoundFiles:ABC]];
break;
case ACA:
[self _playSoundFile:[self _NSStringFromSoundFiles:ACA]];
break;
default:
break;
}
}
/** Dealloc ! **/
-(void) dealloc {
[sae stopBackgroundMusic];
for(id s in soundSources){
//Release source
CDSoundSource *source = [soundSources objectForKey:s];
if(source.isPlaying){ [source stop]; }
[source release];
}
[soundSources release];
for(id m in musicSources){
//Release source
CDLongAudioSource *source = [musicSources objectForKey:m];
if(source.isPlaying){ [source stop]; }
[source release];
}
[musicSources release];
//End engine
[SimpleAudioEngine end];
sae = nil;
//instanceOfMusicLayer = nil;
[super dealloc];
}
#end
Now, I created a new Ccoos2d Helloworld template and added the following to the .m class to test the code:
#import "cocos2d.h"
#import "MusicLayer.h"
enum tags {
MUSICLAYERTAG = 99,
};
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer
{
}
+(CCScene *) scene;
#end
//
// HelloWorldLayer.m
// MusicFadingTest
//
// Import the interfaces
#import "HelloWorldLayer.h"
// HelloWorldLayer implementation
#implementation HelloWorldLayer
+(CCScene *) scene
{
CCLOG(#"In helloworld scene");
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
MusicLayer *musicLayer = [MusicLayer node];
[musicLayer loadPopLevel];
[scene addChild:musicLayer z:-1 tag:MUSICLAYERTAG];
// return the scene
return scene;
}
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
//Add menu items
[CCMenuItemFont setFontSize:20];
CCMenuItemFont *music0Item = [CCMenuItemFont itemFromString:#"Song A" target:self selector:#selector(play:)];
music0Item.tag = 0;
CCMenuItemFont *music1Item = [CCMenuItemFont itemFromString:#"Song B" target:self selector:#selector(play:)];
music1Item.tag = 1;
CCMenuItemFont *music2Item = [CCMenuItemFont itemFromString:#"Song C" target:self selector:#selector(play:)];
music2Item.tag = 2;
CCMenuItemFont *music3Item = [CCMenuItemFont itemFromString:#"Song D" target:self selector:#selector(play:)];
music3Item.tag = 3;
CCMenuItemFont *music4Item = [CCMenuItemFont itemFromString:#"Sound A" target:self selector:#selector(play:)];
music2Item.tag = 4;
CCMenuItemFont *music5Item = [CCMenuItemFont itemFromString:#"Sound B" target:self selector:#selector(play:)];
music3Item.tag = 5;
//Create our menus
CCMenu *menu0 = [CCMenu menuWithItems:music0Item, music1Item, music2Item, music3Item, music4Item, music5Item, nil];
[menu0 alignItemsInColumns: [NSNumber numberWithUnsignedInt:6], nil];
menu0.position = ccp(240,240);
[self addChild:menu0];
}
return self;
}
//Play music callback
-(void) play:(id)sender {
CCNode* node = [self getChildByTag:MUSICLAYERTAG];
NSAssert([node isKindOfClass:[MusicLayer class]], #"not a MusicLayer");
MusicLayer *musicLayer = (MusicLayer*)node;
CCMenuItem *item = (CCMenuItem*)sender;
switch (item.tag ) {
case 1:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 2:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 3:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 4:
[musicLayer playBackgroundMusic:PLAYER_STANDARD];
break;
case 5:
[musicLayer playSoundFile:A];
break;
case 6:
[musicLayer playSoundFile:B];
break;
default:
break;
}
}
- (void) dealloc
{
[super dealloc];
}
#end
But here is the error message that I get when I the simulator I try to press any button and hence trigger the play method in HelloWorld.m
2012-04-23 10:39:18.493 MusicFadingTest[1474:10a03] In _loadMusic
2012-04-23 10:39:18.510 MusicFadingTest[1474:10a03] cocos2d: Frame interval: 1
2012-04-23 10:39:19.405 MusicFadingTest[1474:10a03] *** Assertion failure in -[HelloWorldLayer play:], /Users/user/Desktop/MusicFadingTest/MusicFadingTest/HelloWorldLayer.m:76
2012-04-23 10:39:19.406 MusicFadingTest[1474:10a03] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'not a MusicLayer'
*** First throw call stack:
(0x17f6022 0x1987cd6 0x179ea48 0x11c62cb 0xc264a 0x175c4ed 0x175c407 0x3a3b5 0x3ad73 0x37772 0x17f7e99 0x92821 0x9336f 0x95221 0x8573c0 0x8575e6 0x83ddc4 0x831634 0x27b1ef5 0x17ca195 0x172eff2 0x172d8da 0x172cd84 0x172cc9b 0x27b07d8 0x27b088a 0x82f626 0xc107f 0x2955)
The code above kind of mimics what happenes in the ShootEmUp example but I am missing something. As I cannot get the child by tag..
I asked this question as despite the answer will be probably trivial I hope to get some clarification on the general NON-ModelViewController approach in game programming and the usage of static variables.
I imagine using my MusicLayer in the MainMenu class and in the various layers implementing my levels. I would preload various music files according to the level the player is playing and keep the files that are not level specific preloaded (obviously taking care on the maximum number of sound files supported by the AudioEngine).
The other approach would have been to have a different instance of MusicLayer for each level and initializing them with different music files. The disadvantage of this approach is that the sharedAudioEngine is only one and when you want to have files keep playing between one scene and the other there is the risk of not having a full control on the track numbers used in the sharedAudioEngine (I recall should be 32 caf files for sound effect and few background tracks).
I thus understand why static instances are beneficial in game programming, but still, would love to hear what you think about the biderectional reference in 1 ShootEmUp code.
Also, would like to clarify that I encourage buying 1 as it has been a good starting point.
Thank you very much!
Oh, how much code and letters... First of all, for your question about "static instance". This is not static instance. This is static constructor. So, you can use smth like
CCScene* myScene = [GameScene scene];
instead of
CCScene* myScene = [[GameScene alloc] init];
// doing smth
[myScene release];
So, you will just create an autoreleased instance of your node (in that case, of your scene).
About the music question. You have add your music layer to the scene, but self in your play method will be HelloWorldLayer instance. So if you wanna get your music layer, you can try smth like this
MusicLayer* musicLayer = [[self parent] getChildByTag:MUSICLAYERTAG];
I want to set the class tile value by click on the button
if click onPlus it will have a string value = #"+"
if click onMinus it will have a string value = #"-"
here are the code
//Class Tile
#interface Tile : TouchableNode {
NSString *val;
}
-(void) setVal:(NSString *)v
{
val = v;
}
-(NSString *) getVal
{
return val;
}
And the in another class
I have a code like this
for(Tile *tile in player)
{
if (tile.getVal == #"P") {
if (pauseStatus == 0) {
pauseStatus = 1;
[[CCDirector sharedDirector] pause];
CGSize size = [[CCDirector sharedDirector] winSize];
pauseLayer=[[CCLayer alloc] init];
pauseLayer.anchorPoint=ccp(0,0);
pauseLayer = [CCLayerColor layerWithColor: ccc4(0, 0, 255, 125) width: 300 height: 150];
pauseLayer.position = ccp(size.width/2, size.height/2);
pauseLayer.isRelativeAnchorPoint = YES;
[self addChild: pauseLayer z:8];
//Here are 2 two button that when user click it will have #"+" value or #"-"
plusBtn = [CCMenuItemImage itemFromNormalImage:#"plus.png" selectedImage:#"plus.png" target:self selector:#selector(onPlus:)];
minusBTn = [CCMenuItemImage itemFromNormalImage:#"minus.png" selectedImage:#"minus.png" target:self selector:#selector(onMinus:)];
pauseMenu = [CCMenu menuWithItems:plusBtn, minusBTn, nil];
[pauseMenu alignItemsHorizontally];
[self addChild:pauseMenu z:10];
}
}
}
and I have onPlus & onMinus method that I want to send object tile to that method
-(void)onPlus:(Tile *) set
{
NSString *plus = #"+";
[set setVal:plus];
}
-(void)onMinus:(Tile *) set
{
NSString *minus = #"-";
[set setVal:minus];
}
How to pass the object tile to the method??
Or it have another way to make it??
CCMenuItemImage inherits from CCNode, which has a void* userData property. If you assign tile to userData, you could fetch it back from the (id)sender in your onPlus/onMinus methods.
plusBtn = [CCMenuItemImage itemFromNormalImage:#"plus.png" selectedImage:#"plus.png" target:self selector:#selector(onPlus:)];
plusBtn.userData = (void*)tile; // You may need a bridge cast in ARC
minusBTn = [CCMenuItemImage itemFromNormalImage:#"minus.png" selectedImage:#"minus.png" target:self selector:#selector(onMinus:)];
minusBTn.userData = (void*)tile;
-(void)onPlus:(id)senderObj {
CCNode *sender = (CCNode*)senderObj;
Tile *myTile = (Tile*)sender.userData; // Again you may need a bridge cast here
}
If you want to send a Tile object to your onPlus handler, just subclass CCMenuItemImage and make a Tile property:
#interface MyCustomCCMenuItemImage : CustomCCMenuItemImage
#property (nonatomic, retain) Tile* tile;
#end
Here's a rough example of what your code might look like:
- (void)someMethod
{
for(Tile *tile in player)
{
plusBtn = [MyCustomCCMenuItemImage itemFromNormalImage:#"plus.png" selectedImage:#"plus.png" target:self selector:#selector(onPlus:)];
plusBtn.tile = tile ;
}
}
//Your handler
-(void)onPlus:(id)sender
{
Tile *myTile = sender.tile;
}
For string comparisons, you're better off using
[tile.getVal isEqualToString:#"P"]
Since the method you use only works if it's the exact same string (in the same memory location), as opposed to an equivalent string elsewhere .
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.
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
buttonPressed = NO;
CCMenuItem *myMenuItem = [CCMenuItemImage itemFromNormalImage:#"Icon-72.png" selectedImage:#"Icon-Small.png"target:self selector:#selector(menuSelector:)];
CCMenu *myMenu = [CCMenu menuWithItems:myMenuItem, nil];
myMenu.position = ccp(50, 50);
// add the label as a child to this Layer
[self addChild: myMenu];
}
return self;
}
-(void)menuSelector:(id)sender{
CCSprite *mySprite = [CCSprite spriteWithFile:#"Icon.png"];
mySprite.position = ccp(100, 100);
if (!buttonPressed) {
buttonPressed = YES;
[self addChild:mySprite];
}
else{
[self removeChild:mySprite cleanup:YES];
buttonPressed = NO;
}
}
Why is removeChild:mySprite not working? mySprite still there after i pressed the button(myMenuItem) the second time. Please help me. Thanks.
Every time you touch the button you're creating a new instance of the sprite. You're probably better off creating and adding it as a class variable so you can access it in all methods, then just setting it's visibility on or off as needed.