Cocos2D iPhone - removing the black screen between CCTransition - iphone

I am using cocos2d on my app. I am doing a transition to another scene using
[[CCDirector sharedDirector] replaceScene:
[CCTransitionFadeDown transitionWithDuration:0.5f scene:otherScene]];
On the init part of this other scene, a menu is being built, using CCMenu. This is a full screen menu.
My problem is this: the transition happens to a black screen and then the menu appears. In other words, the transition is being done before the menu is rendered, so, I see an ugly black screen for 0.5 seconds and then, after the transition is done, I see the menu.
To make things clear imagine the first scene has a picture of a boat and the second scene a picture of a car. What I have now is the boat transitioning to black and then the car pops. I need the boat transition to the car.
how do I do that? thanks.
NOTE: I have found this guy with the same problem, but I have tried that solution without success.

This is worth a try, if you are not already using it; it removes black flickers during scene loads. Run the following method before you switch scenes, in case it affects your issue (un-comment these lines and call the method directly):
- (void) removeStartupFlicker
{
//
// THIS CODE REMOVES THE STARTUP FLICKER
//
// Uncomment the following code if you Application only supports landscape mode
//
// CC_ENABLE_DEFAULT_GL_STATES();
// CCDirector *director = [CCDirector sharedDirector];
// CGSize size = [director winSize];
// CCSprite *sprite = [CCSprite spriteWithFile:#"Default.png"];
// sprite.position = ccp(size.width/2, size.height/2);
// sprite.rotation = -90;
// [sprite visit];
// [[director openGLView] swapBuffers];
// CC_ENABLE_DEFAULT_GL_STATES();
}

I have implemented a transition in my menu (same problem), call it 'fadeInView' by adding a black layer on top of everything in the init, with an opacity of 255, and in onEnter I run an action to fade the opacity to 0. as follows:
-(id) init {
self=[super init];
if(self){
// do your stuff
blackShroudLayer_=[CCLayerColor layerWithColor:ccc4(0, 0, 0, 255) width:K_SCREEN_WIDTH height:K_SCREEN_HEIGHT];
[self addChild:blackShroudLayer_ z:500];
}
return self;
}
-(void) onEnter{
// need to [super onEnter] first to that we are running
[super onEnter];
id sh = [CCFadeTo actionWithDuration:K_FADE_TIME opacity:0];
id seq = [CCSequence actions:sh,[CCCallFunc actionWithTarget:self selector:#selector(onUnshroudComplete)], nil];
[blackShroudLayer_ runAction:seq];
}
-(void) onUnshroudComplete{
[blackShroudLayer_ removeFromParentAndCleanup:YES];
}
the constants and blackShroudLayer_ are defined in the class .h file.

Related

Adding a Hud Layer to Scene Cocos2d-3

To keep it simple, what is the easiest way to make the default [ Menu ] in default HelloWorld Scene (for example) as it's own layer. Issue I'm having now is that the scene is completely black, with nothing showing up!
GameLayer node:
- (id)init
{
// Enable touch handling on scene node
self.userInteractionEnabled = YES;
self.theMap = [CCTiledMap tiledMapWithFile:#"AftermathRpg.tmx"];
self.contentSize = theMap.contentSize;
self.metaLayer = [theMap layerNamed:#"Meta"];
metaLayer.visible = NO;
CCTiledMapObjectGroup *objects = [theMap objectGroupNamed:#"mainChar"];
NSMutableDictionary *startPoint = [objects objectNamed:#"startPosition"];
int x = [[startPoint valueForKey:#"x"] intValue];
int y = [[startPoint valueForKey:#"y"] intValue];
self.mainChar = [CCSprite spriteWithImageNamed:#"mainChar.png"];
mainChar.position = ccp(x,y);
[self addChild:mainChar];
[self addChild:theMap z:-1];
[self setCenterOfScreen: mainChar.position];
return self;
}
HudLayer node
-(id)init
{
CGSize winSize = [[CCDirector sharedDirector] viewSize];
CCButton *backButton = [CCButton buttonWithTitle:#"[ Menu ]" fontName:#"Verdana-Bold" fontSize:18.0f];
backButton.position = ccp(0.85f * winSize.width, 0.95f * winSize.height);
[backButton setTarget:self selector:#selector(onBackClicked:)];
[self addChild:backButton];
return self;
}
Scene
+ (GameScene *)scene
{
return [[self alloc] init];
}
- (id)init
{
// Apple recommend assigning self with supers return value
self = [super init];
if (!self) return(nil);
CGSize winSize = [CCDirector sharedDirector].viewSize;
self.gameLayer = [GameLayer node];
[self addChild:gameLayer z:-1];
//self.contentSize = self.gameLayer.contentSize;
hudLayer = [HudLayer node];
hudLayer.position = ccp(winSize.width * 0.9, winSize.height * 0.9);
[self addChild:hudLayer z:1];
return self;
}
From the OP I take that you have two issues with one being that the HUD is not static (i.e. it is moving as your map moves which you don't want) and that it is not positioning at the top of the screen.
Looking at the position issue first, your position is set to normalized. Since the scene's content size has been made to be the size of your map, which I take is larger than your screen, then this is why it is showing up at the top right of the map and not the screen. To fix this don't do normalized positioning. If you want to be able to still express the position in the 0 to 1 range, use (remove the line that sets the position type to normalized also):
CGSize winSize = [[CCDirector sharedDirector] viewSize];
backButton.position = ccp(0.85f * winSize.width, 0.95f * winSize.height);
If your map is 10,000 x 10,000 then using the normalized positioning like you are will set the button to (8,500, 9,500) rather than the top of the screen.
Looking at the static issue next, from the looks of it you have the Hello World scene that you are adding everything to right? It also looks like you are moving the Hello World scene with a call to:
[self setCenterOfScreen: player.position];
What you want to do instead is this, you first have a scene:
HelloWorldScene* scene;
And to this scene you are adding two "main" layers with one being your gameplay layers as children of a main gameplay layer and the other being your HUD layer, which for example could look something like:
GameplayLayer* gameLayer;
HudLayer* hudLayer;
[scene addChild:gameLayer];
[scene addChild:hudLayer];
When the player moves (or camera or whatever), what should be moving is the game's layer, not the root Hello World scene. Moving the root scene will move all of its children, which includes the hud. That is not what you want.
When I worked on, for example, the Goldfish Mysteries app (https://itunes.apple.com/us/app/finn-friends-mysteries/id740040227?mt=8) I had essentially layers for:
Story (comprised of multiple sub-layers like bg, characters, etc)
Text (highlighted text that plays along with the narration audio)
HUD (comprised of multiple sub-layers)
Whenever there is movement on the story level it occurs on the story layer. When the HUD appears then the text and story layers are paused recursively (i.e. them and all of their children) along with the narration audio if any is playing but the HUD layer remains untouched. Resuming consists of resuming story, text, and any playing narration. I don't remember off the top if dropping the hud moved down the story and text layers in this specific app since I don't have my iPad in front of me, but i have done apps in the past where dropping the hud shifted all other layers. In that situation and for a simple app it would be fine to move the scene since the scene would only be shifted enough to show the TOC (in this type of app for example). What you look to be doing is moving the entire scene with the player's movement, which is not what you really wanted to do by the looks of it.
Either way you want a clear separation between layers and operations that are meant to only happen on specific layers should be directed only towards those layers.
Hope this helped.
UPDATE (Edited):
Based on your new edited OP, you have a new problem that you've introduced. For the hud you shouldn't have to set the layer's position since inside the hud layer everything is already being laid out relative to the screen anyways. So what you should have instead is:
hudLayer = [HudLayer node];
[self addChild:hudLayer z:1];
The second issue is that you are not properly writing your init methods for the game and hud layer classes. The init should look like:
- (instanceType)init
{
self = [super init];
if (self)
{
// Do your init stuff...
}
return self;
}
You are never calling:
self = [super init];

CCFollow with CCParticleSystem

I'm trying to follow some sprite by some instance of CCParticleSystem using CCFollow. I don't want to make instance of CCParticleSystem a child of sprite, because I want it to be displayed some time after sprite removed.
When moving sprite from bottom-left to top-right corner my ParticleSystem moves from center to bottom-left corner. I can't understand why does it happen.
Here is sample code:
-(id) init
{
if( (self=[super init]) ) {
CCSprite *someSprite = [CCSprite spriteWithFile:#"Icon.png"];
[self addChild:someSprite];
id action = [CCMoveTo actionWithDuration:5 position:ccp(480,320)];
[someSprite runAction:action];
CCParticleSystemQuad *effect = [CCParticleMeteor node];
// effect.positionType = kCCPositionTypeFree;
// effect.positionType = kCCPositionTypeRelative;
// effect.positionType = kCCPositionTypeGrouped; changing of positionType to any of this options does not make any sense
[effect runAction:[CCFollow actionWithTarget:someSprite]];
[self addChild:effect];
}
return self;
}
It's a case of bad documentation.
I tried this myself and got the same result. By looking at the code and the fragment of documentation that exists, the CCFollow action is intended to be used only on fullscreen nodes like CCLayer, CCScene and perhaps CCNode. I would document it as follows:
CCFollow is an action for scrolling the entire screen by allowing the
layer or scene follow another node, such as a player object. It's an
alternative for moving the Camera object or updating the position
manually.

iOs (cocos2d) screenshot cached or CCRenderTexture buffer? It doesn't change

I using tip from this ask stackoverflow.com/questions/12413460
It's work. My problem is the image not refresh while application not restart or not rebuilding.
I use method for create screenshot. I what have same functional: use touch scree, and screenshot will save into documents directory. User touch second, second screenshot save. And so. But in my app every time only first touch screenshot created, as it cached or buffer ed.
-(UIImage *) screenshotWithStartNode:(CCNode*)startNode
{
[CCDirector sharedDirector].nextDeltaTimeZero = YES;
CGSize winSize = [CCDirector sharedDirector].winSize;
CCRenderTexture* rtx =
[CCRenderTexture renderTextureWithWidth:winSize.width
height:winSize.height];
[rtx begin];
[startNode visit];
[rtx end];
return [rtx getUIImage];
}
I call it when user touch screen (for example)
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
CCNode *n = [[[CCDirector sharedDirector] runningScene].children objectAtIndex:0];
UIImage *img = [self screenshotWithStartNode:n];//this image always same by one, same when user touch screen first time
CCSprite *spriteTemp = [CCSprite spriteWithCGImage:img.CGImage key:#"image"];
[self addChild:spriteTemp];
}
On the screen items have different position in time.
First touch on the screen get right image.But all other touch create screenshots as first image and dose not change.
I think maybe it's buffer CCRenderTexture? or image cache? What i will do?
i try [img release] and try CGImageRelease(img.CGImage) after touch, but it crash app.
You're right, the previous information is being cached. Try a
[[CCTextureCache sharedTextureCache] removeTextureForKey:#"image"];
before you try to overwrite.
Or use difference key names if you need more than one image at once.

Cocos2d - how to make individual particles follow the layer, not the emitter?

I have a CCSprite and a CCParticleSystemQuad that are both children of the CCLayer. In my update method, I set the emitter's position to that of the sprite, so it tracks the sprite around. The smoke puff fall out the bottom of the sprite like you'd expect and even though you move the sprite around, the smoke appears to be part of the background layer.
The problem come if I match up their rotations. Now, for example if my sprite is rocking back and forth, the puffs of smoke swing in an arc and appear attached to the sprite.
How can I make the puffs of smoke continue along the parent layer in a straight line and not rotate with the sprite? They don't translate with the sprite when I move it, so why do they rotate with it?
EDIT: adding code...
- (id)init
{
if (!(self = [super init])) return nil;
self.isTouchEnabled = YES;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
sprite = [CCSprite spriteWithFile:#"Icon.png"]; // declared in the header
[sprite setPosition:ccp(screenSize.width/2, screenSize.height/2)];
[self addChild:sprite];
id repeatAction = [CCRepeatForever actionWithAction:
[CCSequence actions:
[CCRotateTo actionWithDuration:0.3f angle:-45.0f],
[CCRotateTo actionWithDuration:0.6f angle:45.0f],
[CCRotateTo actionWithDuration:0.3f angle:0.0f],
nil]];
[sprite runAction:repeatAction];
emitter = [[CCParticleSystemQuad particleWithFile:#"jetpack_smoke.plist"] retain]; // declared in the header - the particle was made in Particle Designer
[emitter setPosition:sprite.position];
[emitter setPositionType:kCCPositionTypeFree]; // ...Free and ...Relative seem to behave the same.
[emitter stopSystem];
[self addChild:emitter];
[self scheduleUpdate];
return self;
}
- (void)update:(ccTime)dt
{
[emitter setPosition:ccp(sprite.position.x, sprite.position.y-sprite.contentSize.height/2)];
[emitter setRotation:[sprite rotation]]; // if you comment this out, it works as expected.
}
// there are touches methods to just move the sprite to where the touch is, and to start the emitter when touches began and to stop it when touches end.
I found the answer on a different site - www.raywenderlich.com
I don't know why this is true, but it seems that CCParticleSystems don't like to be rotated while you move them around. They don't mind changing their angle property. Actually, there may be cases where you want that behavior.
Anyway I made a method that adjusts the emitter's angle property and it works fine. It takes your touch location and scales the y component to be the angle.
- (void)updateAngle:(CGPoint)location
{
float width = [[CCDirector sharedDirector] winSize].width;
float angle = location.x / width * 360.0f;
CCLOG(#"angle = %1.1f", angle);
[smoke_emitter setAngle:angle]; // I added both smoke and fire!
[fire_emitter setAngle:angle];
// [emitter setRotation:angle]; // this doesn't work
}
CCSprite's anchorPoint is {0.5f, 0.5f), while the emitter descends directly from CCNode, which has an anchorPoint of {0.0f, 0.0f}. Try setting the emitter's anchorPoint to match the CCSprite's.

cocos2d add a 'ready-go!' animation before the game

I want to add a 'ready-go!' animation before game, as well as during game (need to pause the game)
i tried to use [CCDirector sharedDirector] pushScene: [Anim scene]], but although the background of Anim scene is smaller than window size, the transparent area is black instead of see through the scene below.
how can i implement it? (or i don't have to use pushScene at all? )
here is the code for GameScene, onEnter i schedule the 'count' (timer), onExit i unschedule it.
-(void) onEnter {
GameManager* sharedManager = [GameManager sharedManager];
[[CCScheduler sharedScheduler] scheduleSelector:#selector(count) forTarget:sharedManager interval:1.0f paused:NO];
[super onEnter];
}
- (void)onExit {
GameManager* sharedManager = [GameManager sharedManager];
[[CCScheduler sharedScheduler] unscheduleSelector:#selector(count) forTarget:sharedManager];
[super onExit];
}
problem solved. I just create a scene specifically for the 'ready-set-go' animation, pass the necessary parameters to the AnimationScene, then launch the game from the AnimationScene using scheduler.
The result is not as good as 'ready-go' sign on top of GameScene since i am not able to make the transparent effect, but i tried to mimic the background of game scene, and it turns out quite well.
- (void)pauseButtonTapped {
[[CCDirector sharedDirector] pause];
[self pauseSchedulerAndActions];
}
- (void)playButtonTapped {
[[CCDirector sharedDirector] resume];
[self resumeSchedulerAndActions];
}
CCSequence. Before enabling AI timers or whatever runs your game objects, run a CCSequence action.