Cocos2d: Initializing layers by node - class is nil - iphone

I am a Cocos2d beginner so although there is a bit much code here. Some with experience should be able to see what I am doing wrong here. It Crashes on the NSAssert in backgroundLayer when I try to get a instance of the GameBackground class that is added as a child to this layer in the +(id)scene class method. It crashes cause it is Nil (
the GameBackground class). But I thought i initialized it in the +(id)scene method when asking for it´s node? But please correct me, my thinking is fuzzy regarding this matter.
static GameLayer *sharedGameLayer = nil;
+(id) scene
{
CCScene *scene = [CCScene node];
//GameLayer* layer = [GameLayer node];
//[scene addChild:layer];
CCNode *backgroundLayer = [GameBackground node];
[scene addChild:backgroundLayer z:1 tag:LayerTagBackgroundLayer];
CCLayer *cocosObjectsLayer = [HelloWorldLayer node];
[scene addChild:cocosObjectsLayer z:2 tag:LayerTagGameLayer];
CCLayer *userTouchInterface = [GameUserTouchInteface node];
[scene addChild:userTouchInterface z:3 tag:LayerTagUILayer];
return scene;
}
// To access this scene: MultiLayerScene* sceneLayer = [MultiLayerScene sharedLayer];
+ (GameLayer *) sharedLayer
{
NSAssert(sharedGameLayer = nil, #"sharedGameLayer not available.");
//if (sharedGameLayer == nil) sharedGameLayer = [[GameLayer alloc] init];
return sharedGameLayer;
}
-(id) init
{
NSLog(#"init called");
self = [super init];
if (self)
{
NSAssert(sharedGameLayer == nil, #"another GameLayer is already in use!");
sharedGameLayer = self;
// uncomment if you want the update method to be executed every frame
//[self scheduleUpdate];
}
return self;
}
// MultiLayerScene* sceneLayer = [MultiLayerScene sharedLayer];
// To access this scene: GameBackgroundLayer* backgroundLayer = [sceneLayer backgroundLayer];
-(GameBackground *) backgroundLayer
{
CCNode *layer = [self getChildByTag:LayerTagBackgroundLayer];
NSAssert([layer isKindOfClass:[GameBackground class]], #"%#: not a BackgroundLayer!", NSStringFromSelector(_cmd));
return (GameBackground *)layer;
}
Then trying to get an instance of the background layer from another class:
GameBackground *backgroundLayer = [GameLayer sharedLayer].backgroundLayer;
[backgroundLayer runAction:sequence];

I'm guessing the assertion that is failing is.-
NSAssert(sharedGameLayer = nil, #"sharedGameLayer not available.");
You're assigning nil to sharedGameLayer instead of comparing (==). Also, you've commented the line where you set sharedGameLayer. Just guessing here, but you may want to do something like this.-
+(id) scene
{
CCScene *scene = [CCScene node];
sharedGameLayer = [GameLayer node];
[scene addChild:layer];
return scene;
}
Leave your static constructor as clean as possible.
+ (GameLayer *) sharedLayer
{
return sharedGameLayer;
}
-(id) init
{
NSLog(#"init called");
self = [super init];
if (self)
{
CCNode *backgroundLayer = [GameBackground node];
[self addChild:backgroundLayer z:1 tag:LayerTagBackgroundLayer];
CCLayer *cocosObjectsLayer = [HelloWorldLayer node];
[self addChild:cocosObjectsLayer z:2 tag:LayerTagGameLayer];
CCLayer *userTouchInterface = [GameUserTouchInteface node];
[self addChild:userTouchInterface z:3 tag:LayerTagUILayer];
// uncomment if you want the update method to be executed every frame
//[self scheduleUpdate];
}
return self;
}
Create your nodes tree in the init method, and add them as children of your root layer (sharedGameLayer) instead of scene object.

Related

CCLabelTTF value is not updating (cocos2d)

i have two layers control layer which contains CCLabelTTF for score, and first layer in which i detect collision btwn my objects. i want to update score (i.e CCLabelTTF value) in control layer from my first layer.
here is my code...
my scene.m
-(id)init{
self=[super init];
FirstLayer *fl=[FirstLayer node];
[self addChild:fl];
controlLayer *cl=[controlLayer node];
[self addChild:cl z:3 tag:1];
return self;
}
control layer.h
#interface controlLayer : CCLayer{
CCLabelTTF * score ;
int score_value;
}
#property(nonatomic,retain)CCLabelTTF * score ;
#property int score_value;
-(void)displayScore;
#end
controlLayer.m
-(id)init{
// my code..
[self displayScore];
}
return self;
}
-(void)displayScore{
CGSize screenSize=[[CCDirector sharedDirector]winSize];
CCLabelTTF * score_lbl = [CCLabelTTF labelWithString:#"Score" fontName:#"Arial" fontSize:16.0];
score_lbl.position=ccp(screenSize.width*0.10,screenSize.height*0.90);
[self addChild:score_lbl z:99];
score =[CCLabelTTF labelWithString:[NSString stringWithFormat:#"test:%d",score_value] fontName:#"Arial" fontSize:16.0] ;
NSString *str = [score string];
NSLog(#"SCORE control:%#",str);
score.position=ccp(screenSize.width*0.20,screenSize.height*0.90);
[self addChild:score];
}
firstLayer.h
#interface FirstLayer : CCLayer
{
controlLayer *cl;
}
#property(nonatomic,retain)controlLayer *cl;
#end
firstLayer.m
#implementation FirstLayer
#synthesize cl;
-(id)init{
---
cl=[controlLayer new];
[self schedule:#selector(tick:)];
return self;
}
-(void)tick:(ccTime)dt{
bool blockFound=false;
world->Step(dt, 10, 10);
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
// Sprite A = ball, Sprite B = Block
if (spriteA.tag == 1 && spriteB.tag == 2) {
cl.score_value=cl.score_value+5;
[cl.score setString:[NSString stringWithFormat:#"%d",cl.score_value]];
NSString *str = [cl.score string];
NSLog(#"SCORE in GAME:%#",str);
// [cl displayScore];
if (std::find(toDestroy.begin(), toDestroy.end(), bodyB)
== toDestroy.end()) {
toDestroy.push_back(bodyB);
}
}
// Sprite B = block, Sprite A = ball
else if (spriteA.tag == 2 && spriteB.tag == 1) {
cl.score_value=cl.score_value+5;
[cl.score setString:[NSString stringWithFormat:#"%d",cl.score_value]];
NSString *str = [cl.score string];
NSLog(#"SCORE in GAME:%#",str);
// [cl displayScore];
}
}
}
What is going Wrong? , i'm always getting score:test 0 !:(
Your statement cl=[controlLayer new]; is not getting the reference of the controlLayer that was added to your game scene. As your game scene.m code indicates, both FirstLayer and controlLayer instances are initalized and added as children to the CCScene. To get a proper reference to your controlLayer from inside FirstLayer, do it like this:
FirstLayer.m
-(void) onEnter {
[super onEnter];
cl = (controlLayer*)[self.parent getChildByTag:1];
}
The reason you have to put this in onEnter is because self.parent will not be valid within the FirstLayer's init method. onEnter is called after initialization is complete. This should give you the reference to the actual controlLayer that was added to your game scene.
I think by the looks of it, you are sending a new score value to your control layer, but you are not updating the label, as you only set it once. It won't automatically update if score_value changes. I think you need to schedule an update method in your control layer that will update the score label.

Move Image in CocoS2d

I am developing game in Cocos2d. In it background of scene moving constantly with x - axis.
I want to understand how can i do it because i am new in cocos2d.
thanks in advance
Take a look to my code - here are the sources. Find the "moving" technique in HelloWorldLayer.m. I have taken a 360 panorama picture and put it to move continuously from right to left, in fullscreen.
In your YourClass.h file:
#import "cocos2d.h"
#interface YourClass : CCLayer
+(CCScene *) scene;
#end
In your YourClass.m file:
#import "YourClass.h"
#implementation YourClass
CCSprite *panorama;
CCSprite *appendix;
//_____________________________________________________________________________________
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
YourClass *layer = [YourClass node];
// add layer as a child to scene
[scene addChild: layer];
// 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])) {
panorama = [CCSprite spriteWithFile: #"panorama.png"];
panorama.position = ccp( 1709/2 , 320/2 );
[self addChild:panorama];
appendix = [CCSprite spriteWithFile: #"appendix.png"];
appendix.position = ccp( 1709+480/2-1, 320/2 );
[self addChild:appendix];
// schedule a repeating callback on every frame
[self schedule:#selector(nextFrame:)];
}
return self;
}
//_____________________________________________________________________________________
- (void) nextFrame:(ccTime)dt {
panorama.position = ccp(panorama.position.x - 100 * dt, panorama.position.y);
appendix.position = ccp(appendix.position.x - 100 * dt, appendix.position.y);
if (panorama.position.x < -1709/2) {
panorama.position = ccp( 1709/2 , panorama.position.y );
appendix.position = ccp( 1709+480/2-1, appendix.position.y );
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
//_____________________________________________________________________________________
#end
Play a little with this code and you will get what you need.

Cocos2D random sprite movement

As a very beginner in Cocos2D i´m trying to make an iPhone game where some cows move randomly around the screen. I used the code for moving the sprites from here: highoncoding.com/.../. I´m adding the sprites in the init method wia an addAnimal method:
-(void) addAnimal {
animal = [CCSprite spriteWithFile:#"cow.png"];
animal.position = [self generateRandomCoordinates];
[self addChild:animal];
[self startAnimation:animal];
}
My problem:
When i add more than one cow to my game, they move from that random spawn position to another random position and then the first cow stops and the other cow goes on correctly. The startAnimation command in the finishedMoving method goes always to the last sprite. That means i need better control over my sprites but how to da that right?
You can try to implement animal class, that will contain your sprite and incapsulate random movement. Smth like
#implementation Cow
- (id) init
{
self = [super init];
if( self != nil )
{
CCSprite* cowSprite = [CCSprite spriteWithFile:#"cow.png"];
[self addChild: cowSprite];
}
return self;
}
- (void) onEnter
{
[super onEnter];
[self makeRandomMovement];
}
- (void) makeRandomMovement
{
id randomMoveAction = // create your random move action here
id moveEndCallback = [CCCallFunc actionWithTarget: self selector: #selector(makeRandomMovement)];
id sequence = [CCSequence actionOne: randomMoveAction two: moveEndCallback];
[self runAction: sequence];
}
#end
In such way after ending random movement part, method makeRandomMovement will be called again to generate and start new random movement part.
then remake your addAnimal method to smth like
- (void) addAnimal
{
Cow* newCow = [Cow node];
[newCow setPosition: [self generateRandomPosition]];
[self addChild: newCow];
}

Misunderstanding on Cocos2D: Layers, static variables and Model View Controller. Can those coexist?

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];

Cocos2d removeChild not working

// 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.