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 .
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!
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.
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];
// 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.
I have a CCMenuItemSprite that is instantiate to call a function:
`
menuControl = [CCMenu menuWithItems: nil];
//Create the buttons
for(int i=1;i<17;i++)
{
Button *B_image = [Button buttonWithTexture:Sheet.texture type:i];
CCMenuItemSprite *menuButton = [CCMenuItemSprite itemFromNormalSprite:B_image selectedSprite:B_image target:self selector:#selector(generateEnemy:)];
NSNumber *Btype = [NSNumber numberWithInt:i];
menuButton.userData = Btype;
[menuControl addChild:menuButton];
}
[self addChild:menuControl];`
As you can see in the above, I'm using the menuButton.userData to pass in an NSNumber.
This is to "pass a parameter" to the generateEnemy function.
`-(void) generateEnemy:(CCMenuItemSprite*)sender
{
NSNumber *Btype = (NSNumber*)sender.userData;
int Etype = [Btype intValue];
enemy = [[Enemy alloc]init:ccp(300,0) Type:Etype];
}
It crashes at this: NSNumber *Btype = (NSNumber*)sender.userData;
It works for number 1-12. But 13 and above, it crashes.
I've solve this problem quite a while back.
It crashes due to this concept here.
How I pass data is:
for(int i=1;i<17;i++)
{
Button *B_image = [Button buttonWithTexture:Sheet.texture type:i];
CCMenuItemSprite *menuButton = [CCMenuItemSprite itemFromNormalSprite:B_image selectedSprite:B_image target:self selector:#selector(generateEnemy:)];
menuButton.tag = i;
[menuControl addChild:menuButton];
}
In the generateEnemy function:
-(void) generateEnemy:(id) sender
{
enemy = [[Enemy alloc]init:ccp(300,0) Type:[sender tag]];
}