get the current scene returns NULL? - objective-c

I am trying to reload the current scene , and the init is happen then dealloc .
i want to have a condition on the dealloc to return back if it called from the current scene.
i was trying this :
if ([[[CCDirector sharedDirector] runningScene] getChildByTag:16])
return;
and
[[[CCDirector sharedDirector] runningScene] isKindOfClass:[HelloWorldLayer class]]
both gives me NULL, and condition is not true.
why is that ???
my scene looks like :
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *gameScene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
layer.tag=16;
// add layer as a child to scene
[gameScene addChild: layer];
// return the scene
return gameScene;
}
how can i eliminate the dealloc when i only refresh the scene ??

Never do this:
[[CCDirector sharedDirector] replaceScene:self]
or this:
CCDirector* director = [CCDirector sharedDirector];
[director replaceScene:director.runningScene];
You can not replace the current scene simply by passing self or the currently running scene to the replaceScene method. You always have to create a new instance of your CCScene class:
[[CCDirector sharedDirector] replaceScene:[MyScene scene]]
Be careful with the runningScene. The first time you send the runWithScene message to the director, the runningScene will always be nil. You can defer some initialization to the onEnter method:
-(void) onEnter
{
// runningscene is valid in onEnter
[[CCDirector sharedDirector] runningScene];
[super onEnter];
}

Related

How can load a cocos2d scene from view controller class?

1)I made a game using cocos2d-iphone v3.
2)I integrated a fullscreen advertisement.
3)I want to load a cocos2d scene, when user closes ad, but it doesn't work(I imported cocos2d framework).There is just a black screen after advertisement disappears with animation. "interstitialAdDidFINISH" appears in the output, so most likely, that last line is wrong.
-(void)interstitialAdActionDidFinish:(ADInterstitialAd *)interstitialAd {
interstitial = nil;
// [interstitialAd release];
// [ADInterstitialAd release];
requestingAd = NO;
NSLog(#"interstitialAdDidFINISH");
[[CCDirector sharedDirector] replaceScene:[CCBReader loadAsScene:#"MainScene"]];
}
I guess, that I can't load a cocos2d scene from UIViewController class so easy.....
How can I do this?
EDIT: So ? It's not nil
if ( [CCDirector sharedDirector].view != nil) {
NSLog(#"Hey there");
}
EDIT 2: I found out, that [CCDirector sharedDirector]]; is a ViewController too.
I tried something like this . The game crashes, after iAd finished.
[self addChildViewController:[CCDirector sharedDirector]];
[self presentModalViewController:[CCDirector sharedDirector] animated:NO];
[[CCDirector sharedDirector] replaceScene:[CCBReader loadAsScene:#"MainScene"]];
I'v found solution. May be helpfull for others.
First, that's, how I present iad view controller from the MainScene
*ViewControllerAdEx= [[ ViewControllerAd alloc] init]; // making an instance of iad class
[[CCDirector sharedDirector] presentModalViewController:ViewControllerAdEx animated:YES];
[ViewControllerAdEx showFullScreenAd]; // caling a method from class
In the iad class, when iad finishes work:
[self dismissViewControllerAnimated:YES completion:^{
[[CCDirector sharedDirector] replaceScene:[CCBReader loadAsScene:#"MainScene"]];
}];

scheduleOnce not working on my CCLayer class, when set in onEnter

I have a CCLayer class that is a child of a base class I created that is, itself, a child of CCLayer.
After creating this type of class hierarchy, I can not get the onEnter method of my child class to successfully schedule a method call. How can I fix this?
I am trying to get started on a gaming project but I've hit a bit of a brick wall. This seems to be a common problem, however, I can't find a scenario similar to mine and, thus, can't derive a solution.
I have a child CCLayer class that I've created, and it is essentially the exact same thing as the IntroLayer in the Cocos2D, starter iPhone template. In fact, my class is the exact same class with one major exception-- I created a base class that I derived from CCLayer, and that is used as my parent of my IntroLayer.
My problem is that in the onEnter method of my child layer, I see that my transition method is being set in my scheduleOnce assignment. The problem, though, is that my transition method is never being called?
I'm not sure what I am doing incorrectly. The code posted below is my .m file for a my IntroLayer.
IntroLayer.m
#implementation IntroLayer //Child of "MyBaseCCLayer"
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
IntroLayer *layer = [IntroLayer node];
[scene addChild: layer];
return scene;
}
-(void) onEnter
{
//This is calling an overridden onEnter of my base CCLayer.
// See snippet below.
[super onEnter];
CGSize size = [[CCDirector sharedDirector] winSize];
CCSprite *background;
background = [CCSprite spriteWithFile:#"Default.png"];
[background setPosition:ccp(size.width/2, size.height/2)];
[background setZOrder: Z_BACKGROUND];
[self addChild: background];
//In one second transition to the new scene
// I can put a break point here, and this being set.
// It never gets called, though.
[self scheduleOnce:#selector(makeTransition:) delay:1.0];
}
-(void) makeTransition:(ccTime)delay
{
//Here is where I would transition to my menu.
// This is a fresh project, so it's going to a test screen.
// The problem is this method never fires.
[[CCDirector sharedDirector]
replaceScene:[CCTransitionFade
transitionWithDuration:0.5
scene:[TestLayer scene]
withColor:ccBLACK]];
}
-(void) dealloc
{
[super dealloc];
}
#end
MyBaseCCLayer.m
//^boilerplate snip^
-(void) onEnter
{
//Setup overlay
CGSize size = [[CCDirector sharedDirector] winSize];
ccTexParams tp = {GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT};
//overlay is a class variable, CCSprite object.
overlay = [CCSprite spriteWithFile:#"tile_overlay.png" rect:CGRectMake(0, 0, size.width, size.height)];
[overlay retain];
[overlay setPosition: ccp(size.width/2, size.height/2)];
[overlay.texture setTexParameters:&tp];
//Always on top!
[overlay setZOrder:Z_OVERLAY];
}
-(void) dealloc
{
[overlay release];
[super dealloc];
}
As you can see, there is nothing ground breaking going on here. I created this base class because I want to apply the overlay sprite to ever CCLayer in my application. This is why it made sense to create my own base, CCLayer. However, now that I've created my own base class, my IntroLayer won't call my scheduled method that I'm setting in onEnter. How can I get this to work?
Your custom class MyBaseCCLayer is overriding the onEnter method, but you omitted the call to [super onEnter]. It's easy to forget sometimes, but calling [super onEnter] is critical to the custom class. Here is the description of the method from the Cocos2D documentation:
Event that is called every time the CCNode enters the 'stage'. If the
CCNode enters the 'stage' with a transition, this event is called when
the transition starts. During onEnter you can't access a sibling
node. If you override onEnter, you shall call [super onEnter].
If you take a look inside CCNode.m, you will see what happens inside onEnter:
-(void) onEnter
{
[children_ makeObjectsPerformSelector:#selector(onEnter)];
[self resumeSchedulerAndActions];
isRunning_ = YES;
}
So you can see that if you override the onEnter method in your custom class, and you don't call [super onEnter], you miss out on this functionality. Essentially your custom node will remain in a 'paused' state.

Accessing CCLayer headache!

I'm having abit of trouble accessing my layers and its driving me insane. Basically, my layer-scene hierachy is as follows:
Compiler.m - CCLayer - holds the +(CCScene) method and loads all the other CCLayers.
Construct.m - CCLayer - holds the box2d engine and its components
Background.m - CCLayer - holds the Background.
Hud.m - CCLayer - holds the HUD.
within the Compiler implementation class, I add the Scene and all the relevant nodes to the Compiler CCLayer:
#implementation Compiler
+(CCScene *) scene{
Compiler *compiler = [CompileLayer node];
CCScene *scene = [CCScene node];
[scene addChild: compiler];
//Add A Background Layer.
Background *layerBackground = [Background node];
layerBackground.position = CGPointMake(100,100);
[compiler addChild: layerBackground z: -1 tag:kBackground];
//Add The Construct.
Construct *construct = [Construct node];
[compiler addChild: construct z: 1];
//Add A Foreground Layer Z: 2
//After background is working.
//Add the HUD
HudLayer *hud = [Hud node];
[compiler addChild: hud z:3];
}
This all works fine, my layers are added to the compiler and the compiler's scene is accessed by the delegate as predicted.
My problem is that im trying to access my Background CCLayers - CCsprite *background, inside the Construct layer so that I can move it according to the position of my Construct game hero.
I've tried many different methods, but I've currently decided to use a class method rather than an instance method to define CCSprite *background so that I can access it in my Construct Layer.
I've also tried accessing with #properties and initialising an instance variable of the class.
Here is my Background CCLayer:
#implementation Background
-(id) init
{
self = [super init];
if (self != nil)
{
CCSprite *temp = [Background bk];
[self addChild:temp z:0 tag:kBackGround];
}
return self;
}
+(CCSprite *)bk{
//load the image files
CCSprite *background = [CCSprite spriteWithFile:#"background.jpg"];
//get the current screen dimensions
//CGSize size = [[CCDirector sharedDirector] winSize];
background.anchorPoint = CGPointMake(0,0);
background.position = ccp(0,0);
return background;
}
#end
This works, it loads the image to the background layer.
Then finally, I try to access the background image from the Construct Layer.
#interface Construct : CCLayer{
CCSprite *tempBack;
}
#end
#implementation Construct
-(id) init{
tempBack = [Background bk]; //The background equals an instance variable
}
-(void) tick:(ccTime)dt {
tempBack.position.x ++; // To do Something here.
tempBack.opacity = 0.5; // Some more stuff here.
}
#end
This doesnt work, I receive a 'nil' pointer in some respects, tempBack doesnt access the background properly, or at all.
How can I access and modify the Background CCLayers Class Variable +(CCSprite) bk??
It probably doesn't work because your tempBack iVar is autoreleased in compiler layer and you don't retain it. Here's how your init method should look like:
-(id) init{
tempBack = [[Background bk] retain]; //The background equals an instance variable
}
Also - don't forget to release it in dealloc. And I am not sure if it will work either since you'll have different background sprite returned by bk class method (try printing temp and tempBack variable addresses and you'll see).
Update for notifications
In background layer, create the sprite and add it to the layer. Then afterwards add this snippet of code to subscribe to notifications:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateBackground:) name:#"PlayerChangedPosition" object:nil];
Then in construct layer's update method (or any other scheduled method) send this notification with new player coordinates:
[[NSNotificationCenter defaultCenter] postNotificationName:#"PlayerChangedPosition" object:player];
Now in background's layer, implement the updateBackground:(NSNotification *)aNotification method:
- (void)updateBackground:(NSNotification *)aNotification {
// object message returns player object passed when posting notification
CGPoint newCoords = [[aNotification object] position];
// adjust background position according to player position
}

Proper cocos2d scene restart?

How do I restart cocos2d scene and clean memory after the previous one?
[director replaceScene:s] is displaying pink screen. Can't get it to work.
The work-around I made is below, however after closing scene this way app runs very slow and slowly reacts to touch. MainViewController.m:
- (IBAction)startScene {
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
if ([director runningScene]) {
[director end];
works = NO;
}
if (!works) {
EAGLView *glview = [EAGLView viewWithFrame:CGRectMake(0,0, 768, 1024)];
[self.view addSubview:glview];
[director setOpenGLView:glview];
CCLayer *layer = [PlayLayer node];
CCScene *s = [CCScene node];
[s addChild: layer];
[director runWithScene:s];
}
}
Inside PlayLayer.m I go back from scene to view using:
CCDirector *d = [CCDirector sharedDirector];
EAGLView *v = [d openGLView];
[v removeFromSuperview];
Application is displaying images (1280x960x32, imageview inside scrollview) and when user presses the button he can start cocos2d scene. User can then leave from scene back to view (.xib) and continue browsing through images till he finds a new one to start scene with.
I believe this is not a good way to leave scene but everything else I tried is causing app to crash or I keep getting this pink screen 2nd time I start scene. How do I restart it ?
I finally could get rid of pink screen while replacing scene. This is how I am doing it. While adding scene, I check if it's running first time or subsequent times.
MyTestAppDelegate * appDelegate = (MyTestAppDelegate *)[[UIApplication sharedApplication] delegate];
EAGLView *glView;
//check if scene is running first time then create OpenGLView, add to director and run the scene
if ([[CCDirector sharedDirector] runningScene] == NULL) {
if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeDefault];
CCDirector *director = [CCDirector sharedDirector];
glView = [EAGLView viewWithFrame:[appDelegate.window bounds]
pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
[director setOpenGLView:glView];
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
[director setAnimationInterval:1.0/60];
[director setDisplayFPS:YES];
[appDelegate.window addSubview:glView];
[[CCDirector sharedDirector] runWithScene: [MyScene node]];
}
//if scene is to be reloaded, add back the openGLView which was removed when removing the scene
else {
[appDelegate.window addSubview:[[CCDirector sharedDirector] openGLView]];
[[CCDirector sharedDirector] replaceScene:[MyScene node]];
}
For removing currently running scene:
[[CCDirector sharedDirector] replaceScene:[MySceneTwoLayer scene]];
[[CCDirector sharedDirector].openGLView removeFromSuperview];
Adding MySceneTwoLayer is optional. It is an empty scene. I am using it to make sure previous scene was properly removed. And I checked, previous scene's dealloc is properly called.
I hope it helps.
I came across a simliar problem when using Storyboards and when returning to m original UI view which contained my CCGLView from another view.
//when leaving the scene containing the CCGLView.
[CCDirector director] popScene];
then I noticed in viewDIdLoad for that viewcontroller, it would go through this when reinstantiated bt the segue, where you can re add the scene again.
// in viewDidLoad
[CCDirector director] pushScene: theSceneIRemovedEtc];
Admititedly this mightnot work for all cases but certainly did for me..
Ok, found a solution that closes the scene from inside and works well.
While starting scene I add the notification observer in my viewcontroller:
(...)
[director runWithScene:s];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(mySceneEnd:) name:#"scene_ended" object:nil];
That is connected to:
- (void) mySceneEnd:(NSNotification *)notif {
if ([director runningScene])
[director end];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Then in the scene ending method I'm sending the notification to my viewcontroller:
EAGLView *v = [[CCDirector sharedDirector] openGLView];
[v removeFromSuperview];
[[NSNotificationCenter defaultCenter] postNotificationName:#"scene_ended"
object:nil userInfo:nil];
Works really nice, app runs fast after closing scene, but it's an old question/answer and there is surely a better way to do it now.

Add cocos2d scene to UIViewController

I use the following code to add cocos2d scene to a viewcontroller
- (void)viewDidLoad {
self.view = [[UIView alloc] initWithFrame:CGRectMake(0,0,320,480)];
if( ! [CCDirector setDirectorType:CCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:CCDirectorTypeDefault];
[[CCDirector sharedDirector] setPixelFormat:kPixelFormatRGBA8888];
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
[[CCDirector sharedDirector] setDeviceOrientation:kCCDeviceOrientationPortrait];
[[CCDirector sharedDirector] setAnimationInterval:1.0/60];
[[CCDirector sharedDirector] attachInView:self.view];
///adding HelloWorld scene to the view...
[[CCDirector sharedDirector] runWithScene: [HelloWorld scene]];
[super viewDidLoad];
}
And now i need to set the alpha value of self.view....so i did it..
-(void)displaySharePage
{
self.view.alpha=0;
}
But it crashed......don't know why....i got the message..
'A Director was alloced.
setDirectorType must be the first call
to Director'
can anyone help.....advance thanks..
attachInView will be deprecated. Try using setOpenGLView instead. http://www.cocos2d-iphone.org/api-ref/latest-stable/interface_c_c_director.html#a87f9460b05b18b5c7726e1bdcbfe3eca
From the error, it looks like one of two things is happening:
viewDidLoad is being called more than once. You can check this by adding a log statement or breakpoint at the beginning of the method. This will help you find the root cause. You have to make sure that the director code is only called once. One way (not necessarily the right way) is to move the [CCDirector setDirectorType:] call to your app delegate.
You are calling [CCDirector setDirectorType:] somewhere else in your code. This seems unlikely, but doing a search for it would be helpful.
maybe its really getting called twice?
#synthesize window;
- (void)loadView {
// Initialization code
CC_DIRECTOR_INIT();
CCDirector *director = [CCDirector sharedDirector];
//landscape
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
[director setDisplayFPS:YES];
//turn on multi-touch
EAGLView *cocosView = [director openGLView];
[cocosView setMultipleTouchEnabled:YES];
self.view = cocosView;
//default texture formats...
[CCTexture2D setDefaultAlphaPixelFormat:kTexture2DPixelFormat_RGBA8888];
[[CCDirector sharedDirector] runWithScene:[MainGame scene]];
}