CCLabelTTF value is not updating (cocos2d) - iphone

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.

Related

How do I stop endlessly playing SpriteKit emitter?

I am trying to stop emitter playing endlessly in my Sprite Kit project. The Lifetime setting is set to Start=1 and End=0 and it shows correct result that I want (emits only once).
Problem comes when I call it in update method it keeps emitting endlessly, I tried using removeFromParent and removeAllAction but no luck. Can you please help me with the solution here?
-(void)update:(NSTimeInterval)currentTime {
[self updateheartPositionFromMotionManager];
[self matchIt];
if ( _heart.position.x == 512 && _heart.position.y == 484 ){
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:
[[NSBundle mainBundle] pathForResource:#"sparkly" ofType:#"sks"]];
emitter.position = CGPointMake(self.frame.size.width/2,
CGRectGetMidY(self.frame)+100);;
emitter.name = #"exhaust";
emitter.targetNode =_heart;
[self addChild:emitter];
}else{
[self removeFromParent];
}
}
//step 1 take a global variable
#property BOOL sparklyAdded; in your .h file
inside init or instance method of your .m file
sparklyAdded=FALSE;
-(void)particleAdded
{
}
-(void)update:(NSTimeInterval)currentTime {
[self updateheartPositionFromMotionManager];
[self matchIt];
if (( _heart.position.x>= 512 && _heart.position.y=< 484 ) && !sparklyAdded){
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:
[[NSBundle mainBundle] pathForResource:#"sparkly" ofType:#"sks"]];
emitter.position = CGPointMake(self.frame.size.width/2,
CGRectGetMidY(self.frame)+100);;
emitter.name = #"exhaust";
emitter.targetNode =_heart;
[self addChild:emitter];
sparklyAdded=TRUE;
}else{
[self removeEffect];
}
}
-(void) removeEffect
{
[self enumerateChildNodesWithName:#"exhaust" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeAllActions];
[node removeFromParent];
sparklyAdded=FALSE;
}];
}
//remember few thighs while adding particle
1) they are expenisive cpu call so add and remove them only once from update loop

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

How to passing class argument to selector in objective C?

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 .

how to update a value without adding it as a child to a scene

so i am trying to update the score in my game, and the code is running fine but the structure doesnt seem right to me. what I am doing right now is keeping the score in a var and then removing the old child and adding a new child to update the score, something like :
if([self awake]){
int score = (int) x;
//NSLog(#"%i", score);
CCLabelBMFont * scoreLabel = [CCLabelBMFont labelWithString:[NSString stringWithFormat:#"Score : %d", score] fntFile:#"good_dog_plain_32.fnt"];
scoreLabel.position = ccp(100, 300);
[scoreScene addChild:scoreLabel];
if([_game getChildByTag:123]){
[_game removeChildByTag:123 cleanup:YES];
}
[_game addChild:scoreScene z:99 tag:123];
}
now this code works just fine but its ruining the fps for the game!!! is there anyway I can update the value of scoreLabel without having to remove and then add the score to the main scene of the game?
thanks
UPDATED CODE
final fixed code :
in the main layer of the game I added
CCLabelBMFont * scoreLabel;
in the header file.
in the main init of the game I added
int score = 0;
scoreLabel = [CCLabelBMFont labelWithString:[NSString stringWithFormat:#"Score : %d", score] fntFile:#"good_dog_plain_32.fnt"];
CCScene * scoreScene = [CCScene node];
scoreLabel.position = ccp(100, 300);
[scoreScene addChild:scoreLabel];
[self addChild:scoreScene z:99 tag:123];
and then simply used setString in my method to update the score like :
[_game.scoreLabel setString:[NSString stringWithFormat:#"Score : %d", score]];
note that _game is because I declared the scoreLabel in the main scene, and my method is running in another file, so if you have your highscore method in the same file, there is no need for _game
The CCLabelBMFont implement the CCLabelProtocol, so it's respond to the setString: method.
You just have to had your "scoreLabel" in the init method and then update the score like that:
if([self awake])
{
int score = (int) x;
//NSLog(#"%i", score);
[scoreLabel setString:[NSString stringWithFormat:#"Score : %d", score]];
}
EDIT
It can looks like that:
- (id)init
{
if ((self = [super init]))
{
self.score = 0;
CCLabelBMFont *scoreLabel = [CCLabelBMFont labelWithString:[NSString stringWithFormat:#"Score : %d", score] fntFile:#"good_dog_plain_32.fnt"];
scoreLabel.position = ccp(100, 300);
[self addChild:scoreLabel z:1 tag:123];
}
}
- (void)updateScore
{
CCLabelBMFont *scorelabel = (CCLabelBMFont *)[self getChildByTag:123];
[scorelabel setString:[NSString stringWithFormat:#"Score: %d",self.score]];
}
Use CCLabelAtlas instead and use setString to change the string of the CCLabelAtlas object.

How to have a Score Integer updated and displayed in Cocos2d?

I am obviously making a game that has a score. How do I call an update method and have the integer actually displayed in the Top-Right corner?
Here, this might work
In the .h file:
#interface HelloWorld : CCLayer {
int score;
CCLabelTTF *scoreLabel;
}
- (void)addPoint;
In the .m file:
In the init method:
//Set the score to zero.
score = 0;
//Create and add the score label as a child.
scoreLabel = [CCLabelTTF labelWithString:#"8" fontName:#"Marker Felt" fontSize:24];
scoreLabel.position = ccp(240, 160); //Middle of the screen...
[self addChild:scoreLabel z:1];
Somewhere else:
- (void)addPoint
{
score = score + 1; //I think: score++; will also work.
[scoreLabel setString:[NSString stringWithFormat:#"%#", score]];
}
Now just call: [self addPoint]; whenever the user kills an enemy.
That should work, tell me if it didn't because I have not tested it.
in header file:
#interface GameLayer : CCLayer
{
CCLabelTTF *_scoreLabel;
}
-(void) updateScore:(int) newScore;
in implementation file:
-(id) init
{
if( (self=[super init])) {
// ..
// add score label
_scoreLabel = [CCLabelTTF labelWithString:#"0" dimensions:CGSizeMake(200,30) alignment:UITextAlignmentRight fontName:#"Marker Felt" fontSize:30];
[self addChild:_scoreLabel];
_scoreLabel.position = ccp( screenSize.width-100, screenSize.height-20);
}
return self;
}
-(void) updateScore:(int) newScore {
[_scoreLabel setString: [NSString stringWithFormat:#"%d", newScore]];
}
EDIT: if you don't want to use an ivar, you can use tags:
[self addChild:scoreLabel z:0 tag:kScoreLabel];
// ...
CCLabelTTF *scoreLabel = (CCLabelTTF*)[self getChildByTag:kScoreLabel];
EDIT 2: For performance reasons you should switch to CCLabelAtlas or CCBitmapFontAtlas if you update the score very frequently.
Also read the cocos2d programming guide about labels.
Using UILabel
UILabel.text = [NSString stringWithFormat:#"%lu",score];
Move the UILabel in the top of the view using interface builder
you could also create it programmatically
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,500,30)];
[[self view] addSubview:label];
[label release]; // dont leak :)