how push scene in cocos2d and pass parameters - iphone

i want know if there is a way to push a scene in cocos2d 2.0 and pass some parameter to this pushed scene, for example, i know that to push a scene i use this:
[[CCDirector sharedDirector] pushScene:[HelloWorldLayer scene]];
and this push the helloworldlayer, that is a simple layer:
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer
{
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
but i want pass to this layer some parameter, so when the layer is pushed i can use the parameter i passed.
how i can do it?

you can do something like +(CCScene *) sceneWithParameter:(ParameterType)parameter; instead of +(CCScene *) scene;

First you will have to create a method to call with the parameter like so
HelloWorldLayer.h
#interface HelloWorldLayer : CCLayer
{
}
+(CCScene *)sceneWithParam:(id)parameter;
#end
HelloWorldLayer.m
#implementation HelloWorldLayer
+(CCScene *)sceneWithParam:(id)parameter
{
[[parameter retain]doSomething];
CCScene * scene = [CCScene node];
HelloWorldLayer *layer = [HelloWorldLayer node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if(self = [super init])
{
}
return [super init];
}
// All your methods goes here as usual
#end
Then you push it by calling
[[CCDirector sharedDirector] pushScene:[HelloWorldLayer sceneWithParam:obj]];
Now this might still not be enough, if you need the parameter inside your layer you will need to do the same thing for the layer. Create the initmethod with the method and then pass it further to the layer in the sceneWithParam: method.

Do it through global variable or external object (which is accessible from "pusher" and HelloWorldLayer. Or even you can pass the parameter through userData property of HelloWorldLayer.

Johnathan's and Kreiri's answers work well, but couldn't you also just add properties to your pushed scene and set them? So the code would look something like this (typed in browser, you may have to tweak):
HelloLayer *hello = [HelloLayer scene];
hello.param1 = someValue;
hello.param2 = someOtherValue;
hello.param3 = yetAnotherValue;
[[CCDirector sharedDirector] pushScene: hello];
Of course, in HelloLayer.h, you'd define something like:
#property (strong, nonatomic) NSString *param1;
#property (readwrite) BOOL *param2;
#property (strong, nonatomic) NSNumber *param3;

Related

passing value from one scene to another

I've looked it up and most of the information is on making a singleton class. I just want to pass one value to my helloworldlayer.m from my menuscreen.m. What is the easy way to do this.
- (void) changeScenePlay
{
CCScene *testscene = [HelloWorldLayer scene];
testscene.number = 1;
[[CCDirector sharedDirector] replaceScene:testscene];
}
One of the methods i have tried is something like this, but this isnt working for me either.
how do i access a variable in a scene and change it.
Use variable member in HelloWorld layer class.
#interface HelloWorldLayer: CCLayer
{
int number;
}
#property(nonautomatic, assign) int number;
#end
#implementation HelloWorldLayer
#synthesize number;
+(CCScene *) sceneWithNumber:(int)inNumber
{
CCScene *scene = [CCScene node];
HelloWorldLayer *layer = [[[HelloWorldLayer alloc] initWithId:inNumber] autorelease];
[scene addChild: layer z:Z_SCENE tag:TAG_SCENE];
return scene;
}
-(id) initWithId:(int)inNumber
{
if( (self=[super init])) {
self.number= inNumber;
}
return self;
}
#end
Call sceneWithNumber instead of scene
- (void) changeScenePlay
{
CCScene *testscene = [HelloWorldLayer sceneWithNumber:1];
[[CCDirector sharedDirector] replaceScene:testscene];
}
create extern variable for this and do as you are doing.

Cocos2d Game crashing when replacing scene (Code Included)

so something is going haywire with my Cocos2d Game. I recently added in some code that would allow me to have a dialog message at the beginning of certain levels in my game. And whenever I go to restart a level (Which replaces the scene) of a level that has the dialog cclayer added to the current scene, I get a BAD ACCESS error through the iPhone simulator, and then if I run it on my iPhone 4S I'm getting a SIGARBT error that says
"* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'child already added. It can't be added again'"
This is my first cocos2d game I'm making. I've toyed a lot with cocos2d and have what I believe is a basic understanding. I have a lot of programming experience with other languages, but I only started learning objective-c about 3 months ago.
Here is my code.
GameDialog.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "GameData.h"
#interface GameDialogLayer : CCLayer {
NSString *dialogText;
}
#property (nonatomic, retain) NSString *dialogText;
+(CCScene *) scene;
-(void) addDialogWithText: (NSString *)text;
#end
GameDialog.m
#import "GameDialogLayer.h"
#implementation GameDialogLayer
#synthesize dialogText;
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
GameDialogLayer *layer = [GameDialogLayer node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
// init the bg overlay
CCLayerColor *backgroundLayer = [CCLayerColor layerWithColor:ccc4(0, 0, 0, 175)];
[self addChild:backgroundLayer z:0];
// init the dialog box
CCSprite *dialogBox = [CCSprite spriteWithFile:#"dialog.png"];
dialogBox.position = ccp([[CCDirector sharedDirector] winSize].width / 2, [[CCDirector sharedDirector] winSize].height / 2);
[self addChild:dialogBox];
// init the dialog menu
CCMenuItemImage *dialogOkay = [CCMenuItemImage itemWithNormalImage:#"dialogOkayButton.png" selectedImage:#"dialogOkayButtonPressed.png" target:self selector:#selector(okayButton)];
CCMenu *dialogMenu = [CCMenu menuWithItems:dialogOkay, nil];
dialogMenu.position = ccp(dialogMenu.position.x, dialogMenu.position.y - 118);
[self addChild:dialogMenu];
}
return self;
}
-(void) okayButton
{
[GameData sharedGameData].isPaused = 0;
[[self parent] schedule:#selector(startCountdown:) interval:1];
[[self parent] removeChild:self cleanup:YES];
}
-(void) addDialogWithText: (NSString *)text
{
CCLabelTTF *dialogTextLabel = [CCLabelTTF labelWithString:text dimensions:CGSizeMake(180, 250) hAlignment:kCCTextAlignmentLeft fontName:#"MarkerFelt-Thin" fontSize:20];
dialogTextLabel.color = ccBLACK;
dialogTextLabel.position = ccp([[CCDirector sharedDirector] winSize].width / 2, [[CCDirector sharedDirector] winSize].height / 2);
[self addChild:dialogTextLabel];
}
- (void) dealloc
{
[super dealloc];
}
#end
And then the dialog is being added to my dialog property in my levelData class that is istantiated for each level and holds the properties of the level.
Here is the property holding it
#property (nonatomic, retain) GameDialogLayer *dialog;
And here is how I'm adding the dialog to the levelData dialog property
-(void) addDialog: (NSString *)dialogText
{
dialog = [GameDialogLayer node];
[dialog addDialogWithText:dialogText];
}
You are adding a node to the scene twice somehow, I had a look at your code and couldn't spot it but they are easy to find. Just add a breakpoint for all Objective C exceptions. The debugger will stop on the line causing the problem.
See here for how to add the breakpoint. You can change "Exception: All" to "Exception: Objective C" to avoid exceptions from CocosDenhesion if you are using it.

How to access sprites from a different class

I'm trying to make an app and I am currently trying to separate data into different files.
I will give some code from the main class (ArcherClass) and the player class.
This is the player class:
Player.h______________________
#interface Player:CCLayer{
CCSprite *_head;
}
#property (nonatomic, retain) CCSprite *head;
-(void)setHead:(CCSprite *)head;
+(id)player;
#end
Player.m______________________
#implementation Player
#synthesize head = _head;
+(id)player{
Player *playerSprite = nil;
return playerSprite;
}
-(void)setHead:(CCSprite *)head{
_head = [CCSprite spriteWithFile:#"head.png"];
}
#end
ArcherClass.m______________________
#import "ArcherClass.h"
#import "cocos2d.h"
#import "Player.h"
#implementation ArcherClass
//skip some stuff...
+(id) scene{
CCScene *scene = [CCscene node];
CCLayer* layer = [ArcherClass node];
[scene addChild:layer];
return scene;
}
-(id) init{
if(self = [super init]{
self.isTouchEnabled = YES;
}
Player *player = [[Player alloc]init];
CCSprite *bob = player.head;
bob.position = ccp(250,250);
//There are layers in this class, so this may be a problem
}
I had the app up and working, everything worked previously because everything was local to the ArcherClass. There were no other classes. Now I am trying to make seperate classes for everything, but I am running into problems. How can I fix this?

How to deallocate a CCLayer

I am making a box2d app for the iphone using cocos2d. I am trying to switch my CCLayer going from my HelloWorldLayer to my HomeScene and I get an error "Thread 1: Program received signal: "EXC_BAD_ACCESS"." It gives me that error when I try to call [super dealloc] in my dealloc method for my HelloWorldLayer. Please Help. Here is my .h and .mm
#interface HelloWorldLayer : CCLayer
{
b2World *world;
Cannon *cannon1;
Cannon *cannon2;
Cannonball *cannonball1;
Cannonball *cannonball2;
float theMass;
float theMass2;
CCSprite *sunBack;
b2Vec2 cannon1Pos;
b2Vec2 cannon2Pos;
CCMenuItemSprite *pauseBut;
CCMenuItemSprite *playBut;
CCMenu *pauseMenu;
}
#property(nonatomic)b2Vec2 cannon1Pos;
#property(nonatomic)b2Vec2 cannon2Pos;
#property(nonatomic, retain)CCSprite *sunBack;
#property(nonatomic, retain)Cannon *cannon1;
#property(nonatomic, retain)Cannon *cannon2;
#property(nonatomic, retain)Cannonball *cannonball1;
#property(nonatomic, retain)Cannonball *cannonball2;
#property(nonatomic, retain)CCMenu *pauseMenu;
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
+(HelloWorldLayer *) sharedLayer;
-(void)createMonkeys;
-(void)restartGame;
-(void)playGame;
-(void)pauseGame;
-(CCSpriteBatchNode*)getSpriteBatch;
-(void)goToHome;
#end
Here is my .mm where I deallocate
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
delete world;
world = NULL;
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[cannon1 removeFromParentAndCleanup:YES];
[cannon2 removeFromParentAndCleanup:YES];
[cannonball1 removeFromParentAndCleanup:YES];
[cannonball2 removeFromParentAndCleanup:YES];
// don't forget to call "super dealloc"
[super dealloc];
}
You can try this one instead of the dealloc you have used
-(void) dealloc
{
delete world;
world = NULL;
cannon1 = NULL;
cannon2 = NULL;
cannonball1 = NULL;
cannonball2 = NULL;
[super dealloc];
}

Pass level time to Congratulations scene cocos2d

Just wondered if someone could quickly give me a hand. Im building a simple puzzle slider game, ive added a timer to the game now and all i want to do is pass the time it took to complete the puzzle over to the congrats scene. Could anyone just have a look at my code see where im going wrong please?
play.h
#import "cocos2d.h"
#import "Box.h"
#interface PlayLayer : CCLayer
{
int timeInt;
int secs;
int mins;
CCLabel *timeLabel;
NSString *TotalTimeString;
}
#property (nonatomic, assign) int timeInt;
#property (nonatomic, assign) int secs;
#property (nonatomic, assign) int mins;
#property (nonatomic, retain) NSString *TotalTimeString;
#end
Play.m
#import "PlayLayer.h"
#import "Congrats.h"
#implementation PlayLayer
#synthesize timeInt;
#synthesize secs;
#synthesize mins;
#synthesize TotalTimeString;
-(id) init{
self = [super init];
TotalTimeString = [NSString stringWithFormat:#"Time: %02d:%02d", mins, secs];
timeLabel = [[CCLabel labelWithString:TotalTimeString dimensions: CGSizeMake(130,27) alignment: UITextAlignmentCenter fontName:#"Marker Felt" fontSize:25.0] retain];
timeLabel.position = ccp(155,430);
[self addChild:timeLabel];
[self schedule: #selector(tick2:) interval:1.0];
return self;
}
-(void) tick2: (id) sender{
timeInt++;
secs = timeInt % 60;
mins = timeInt / 60;
[timeLabel setString: TotalTimeString];
}
-(void) check: (id) sender data: (id) data{
int valueToCheck = 0;
bool breakOut = false;
for (int y=0; y < box.size.height && !breakOut; y++) {
for (int x=0; x < box.size.width && !breakOut; x++) {
Tile *tile = [box objectAtX:x Y:y];
if (tile.check != [box hashOfXY:x y:y]) breakOut = true;
valueToCheck++;
}
}
int totalTiles = box.size.width * box.size.height;
if (valueToCheck == totalTiles){
[[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:1 scene:[Congrats node]]];
CCScene *scene = [Congrats scene];
scene.TotalTimeTakenWhenDiedString = TotalTimeString;
[[CCDirector sharedDirector] replaceScene:scene];
}
}
#end
Congrats.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "PlayLayer.h"
#interface Congrats : CCLayer {
CCLabel *timeLabel;
NSString *TotalTimeTakenWhenDiedString;
}
#property (nonatomic, assign) NSString *TotalTimeTakenWhenDiedString;
+(id) scene;
#end
Congrats.m
#import "Congrats.h"
#import "DifficultyLevel.h"
#import "PlayLayer.h"
#import "PlayLayerMedium.h"
#import "PlayLayerHard.h"
#import "MainMenu.h"
#implementation Congrats
#synthesize TotalTimeTakenWhenDiedString;
+(id) scene {
// ‘scene’ is an autorelease object.
CCScene *scene = [CCScene node];
// ‘layer’ is an autorelease object.
Congrats *layer = [Congrats 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] )) {
timeLabel = [[CCLabel labelWithString: TotalTimeTakenWhenDiedString dimensions: CGSizeMake(130,27) alignment: UITextAlignmentCenter fontName:#"Marker Felt" fontSize:25.0] retain];
timeLabel.position = ccp(155,430);
}
return self;
}
-(void) goToGameplay: (id) sender {
[[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:1 scene:[MainMenu node]]];
}
}
#end
I was kind of hoping that, that would work. But i keep getting this error "error: request for member 'TotalTimeTakenWhenDiedString' in something not a structure or union"
Anyone help at all?
Cheers
Your problem is here:
CCScene *scene = [Congrats scene];
scene.TotalTimeTakenWhenDiedString = TotalTimeString;
The object scene is of type CCScene and you are trying to access TotalTimeTakenWhenDiedString on it. But that is a property of Congrats, not of CCScene.
Here's a suggestion which might work, by getting the Congrats object out from scene. (Caveat: I'm not familiar with cocos2d, and I only looked it up quickly here. This may be a terrible way to be doing it!)
Change here:
// add layer as a child to scene
[scene addChild:layer z:0 tag:1]; // tag it with 1
and here:
CCScene *scene = [Congrats scene];
Congrats *congrats = (Congrats *)[scene getChildByTag:1]; // get the congrats child object, which we tagged 1
congrats.TotalTimeTakenWhenDiedString = TotalTimeString;
[[CCDirector sharedDirector] replaceScene:scene];
... Why dont you just create a singleton class which you can use as a global object? and access/update it anywhere in the game?