Cocos2d restart current scene - cocos2d-iphone

I've got an end of level layer added to game, each level has its own scene. I want to be able to restart the current scene. Obviously the scene will change but the layer will remain the same. How is this done. I've tried-
CCScene *currentScene = [[CCDirector sharedDirector]runningScene];
[[CCDirector sharedDirector]replaceScene:currentScene];
Thanks

This does not work because you can't replace the same scene object with itself:
CCScene *currentScene = [[CCDirector sharedDirector]runningScene];
[[CCDirector sharedDirector]replaceScene:currentScene];
Instead you have to create a new instance of your scene, like so:
[[CCDirector sharedDirector] replaceScene:[YourSceneClass scene]];
If you don't know what the current scene class is, then this ought to work:
CCScene *currentScene = [CCDirector sharedDirector].runningScene;
CCScene *newScene = [[[currentScene class] alloc] init];
[[CCDirector sharedDirector] replaceScene:newScene];
Assuming you're using ARC as everyone should these days. Otherwise add an autorelease.

I ran into the same problem. I tried this
CCScene *currentScene = [CCDirector sharedDirector].runningScene;
CCScene *newScene = [[[currentScene class] alloc] init];
[[CCDirector sharedDirector] replaceScene:newScene];
and it gave me a blank screen.
The problem is this line
CCScene *newScene = [[[currentScene class] alloc] init];
[currentScene class] actually returns CCScene..
Hence
[CCScene alloc] init] gives us a blank screen.
The way how I got around this problem was by setting tag for each of my scene class.
For example:
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
scene.tag = 1;
// 'layer' is an autorelease object.
GameOneLayer * layer = [[[GameOneLayer alloc] init];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
Hope this helps.

Related

removing texture

I am having some problem with removing my spritesheets after my scene exit..
I basically follow Ray's instructions by removing unused textures in init
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[[CCTextureCache sharedTextureCache] removeUnusedTextures];
[[CCDirector sharedDirector] purgeCachedData];
and in dealloc I have
CCTexture2D * texture = spriteNode.textureAtlas.texture;
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromTexture:texture];
[[CCTextureCache sharedTextureCache] removeTexture:texture];
Reference
This works fine if the transition to scene is not the current scene.
But when I tried to "restart" the current scene it crashes.
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:2.0 scene:[currentScene scene] withColor:ccBLACK]];
Seems like the problem is that when replacing the current scene by the "new" current scene.. the "new" current scene init is called before the current scene is deallocated. Hence my "new" current scene spritesheet got deallocated right after it's being created.
How can I properly remove the spritesheets in this case?
Or is my approach to restart the current scene incorrect?
Update:
I was trying to add a loading scene as advised but couldn't make it work.. here's my loading scene
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
LoadingLayer * layer = [[[LoadingLayer alloc]init]autorelease];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init{
if(self = [super init]){
winSize = [CCDirector sharedDirector].winSize;
CCLabelTTF * loadingLabel = [CCLabelTTF labelWithString:#"Loading..." fontName:#"Marker Felt" fontSize:30];
}
loadingLabel.position = ccp(winSize.width/2, winSize.height/2);
[loadingLayer addChild:loadingLabel];
[self scheduleUpdate];
}
return self;
}
-(void) update:(ccTime)dt{
[self unscheduleUpdate];
NSString * bgPlist = #"backgroundsIPAD.plist";;
NSString * hudPlist = #"hudSpritesIPAD.plist"
NSString * gameOnePlist = #"gameOneSpritesIPAD.plist";
// load background
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:bgPlist];
// load hud
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:hudPlist];
// load sprites
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:gameOnePlist];
[[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:2.0 scene:[GameTwoLayer scene] withColor:ccBLACK]];
}
This will give me a splash of the GameTwoLayer in this case, then a black screen..
What am I doing wrong?
Indeed, the new scene's init runs before your current scene is deallocated. Makes sense, since you're creating a new scene object within your current scene object.
You should not remove textures etc during init for that reason. It's just not the right place to do so. The scene that has been using the resources should be responsible for removing those resources. Removing textures etc should be done in the dealloc method of the scene.
If you need to avoid the memory spike with both scenes in memory at the same time, you'll have to add an in-between "loading" scene so that the previous scene will be deallocated before the next scene starts.

CCRepeatForever action stops automatically while switching scenes

I am developing a simple game app.
I am required to have two screens one on left & other on right, just like on scroll view with pagination having 2 screens to toggle between. I am detecting swipes with the help of RootViewController class and successfully swiping left and right screens.
But the problem is I have to have infinitely running rotateBy action on both screens each running this action on single sprite placed on center of screen.
I am using a main scene called SelectWorld & two are it's sub scenes.
First screen is called factory & other is called stack.
Following is my code:
Select World Screen:
#implementation SelectWorld
+(CCScene*)scene
{
CCScene* scene = [CCScene node];
SelectWorld* layer = [SelectWorld node];
[scene addChild:layer];
return scene;
}
-(id)init
{
if((self = [super init]))
{
[RootViewController singleton].swipeCallBackHandler = self;
[[RootViewController singleton] enableSwipeDetection];
factory = [[ColorfulFactory scene] retain];
stack = [[CoinsStack scene] retain];
[self addChild:factory];
isOnFactory = YES;
}
return self;
}
-(void)swipedLeft
{
if(isOnFactory)
{
[self removeAllChildrenWithCleanup:YES];
isOnFactory = NO;
CCTransitionScene* transitionalScene = [CCTransitionSlideInR transitionWithDuration:0.3 scene:stack];
[[CCDirector sharedDirector] replaceScene:transitionalScene];
}
}
-(void)swipedRight
{
if(!isOnFactory)
{
[self removeAllChildrenWithCleanup:YES];
isOnFactory = YES;
CCTransitionScene* transitionalScene = [CCTransitionSlideInL transitionWithDuration:0.3 scene:factory];
[[CCDirector sharedDirector] replaceScene:transitionalScene];
}
}
Here's the code for factory only, same goes for stack-
#implementation ColorfulFactory
+(CCScene*)scene
{
CCScene* scene = [CCScene node];
ColorfulFactory* layer = [ColorfulFactory node];
[scene addChild:layer];
return scene;
}
-(id)init
{
if((self = [super init]))
{
CGSize size = [CCDirector sharedDirector].winSize;
CCLabelTTF* info = [CCLabelTTF labelWithString:#"Colorful Factory" fontName:#"Helvetica" fontSize:35.0];
info.position = ccp(size.width/2, size.height-50);
[self addChild:info];
CCSprite* sprite = [CCSprite spriteWithFile:#"Icon.png"];
sprite.position = ccp(size.width/2, size.height/2);
[self addChild:sprite];
sprite.tag = 123;
CCRotateBy* rotateBy = [CCRotateBy actionWithDuration:60 angle:360.0];
CCRepeatForever* repeatForever = [CCRepeatForever actionWithAction:rotateBy];
[sprite runAction:repeatForever];
repeatForever.tag = 456;
}
return self;
}
Now the problem is, in first two swipes actions are running fine, but as soon as I try to swipe more than two or three times, actions got stopped. I haven't written a single line to stop this in both the classes. I require it there running forever.
Your mistake is that you're re-using the same scene object for every scene change. You can't do that in cocos2d, you have to create a new instance of the scene object.
You should never retain a scene object, since there ought to be only one active scene at any one time (with the exception of the duration of a scene transitioning to another scene - but that's handled by cocos2d internally).
For example:
CCScene* factory = [ColorfulFactory scene];
CCTransitionScene* transitionalScene = [CCTransitionSlideInR transitionWithDuration:0.3 scene:factory];
[[CCDirector sharedDirector] replaceScene:transitionalScene];
If you need both objects in memory, make them layers (or nodes), not scenes. Don't use replaceScene but instead animate the layers with regular moveTo actions in and out of the screen.

ccscene arc and autorelease

I am trying to construct a number of scenes in my GameManager singleton init.
The scene is created via
- (id)init
{
self = [super init];
if (self) { // 'mainScene' is an autorelease object.
mainScene = [CCScene node];
...
}
GameManger holds a strong reference to mainScene:
#interface GameManager : NSObject
{
CCScene* mainScene;
}
But if I try to push the scene later with
[[CCDirector sharedDirector] pushScene:mainScene];
I get EXC_BAD_ACCESS
If I create and immediately push then everything works. Shouldn't the default __strong reference keep the object allocated?
Thanks in advance for any help....
Figured it out ... [CCScene node] is a convenience factory method that does:
[[[self alloc] init] autorelease];
but since I am using arc...I dont want that - I want
mainScene = [[CCScene alloc]init];
instead of
mainScene = [CCScene node];

cocos2d start / stop running Scene

I'm trying to load one scene. This runs fine the first time, but when I try to reload again appears a white square where the animation is placed.
This is the code to start and stop the scene. What I'm missing?
thanks.
-(void)runScene:(OTAnimationCC2d *)animation
{
scene = [CCScene node];
[scene addChild:animation];
if ([[[CCDirector sharedDirector] runningScene] isRunning])
{
[[CCDirector sharedDirector] replaceScene:scene];
}
else
{
[[CCDirector sharedDirector] runWithScene:scene];
}
}
-(void)stopScene
{
[[[CCDirector sharedDirector] runningScene] stopAllActions];
[[[CCDirector sharedDirector] runningScene] removeAllChildrenWithCleanup:YES];
[[CCDirector sharedDirector] pushScene:scene];
}
Why not just call [self runScene] at the end of stopScene rather than [[CCDirector sharedDirector] pushScene:scene]? It sounds like you want the scene to reload fresh, which your runScene already does when it calls replaceScene.
Either way you should be creating a new scene node and using replaceScene (which is being done in runScene and is why I recommend just calling that).

Switching scenes with inputs

Here's the code I know to change scenes with cocos2d:
[[CCDirector sharedDirector] replaceScene:[HelloWorld scene]];
But I wonder if it's possible I can switch the scenes with some parameters.
I tried this method:
HelloWorld *scene = [HelloWorld scene];
[scene initWithInput:0];
[[CCDirector sharedDirector] replaceScene:scene];
-(void)initWithInput:(int)input is what I wrote for test in HelloWorld class.
And it does't work, does any one know how to do it?
Try overriding the scene method. Something like
+(id) sceneWithInput:(int) i
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorld *layer = [HelloWorld nodeWithInput:i];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
Then you would call
[[CCDirector sharedDirector] replaceScene:[HelloWorld sceneWithInput:0]];