I just started learning Cocos2dx, and use the basic HelloWorld project. I added a SecondScene and and a button in it to change scene. But once the popScene method executed, screen became black and it didn't pop to first scene. I don't have any idea what's wrong.
Here's the code I modified a little in HelloWorld.cpp:
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
CCDirector::sharedDirector()->pushScene(SecondScence::scene());
#endif
Code in SecondScene:
#include "SecondScence.h"
USING_NS_CC;
CCScene* SecondScence::scene(){
CCScene* scene=CCScene::create();
SecondScence* layer = SecondScence::create();
scene->addChild(layer);
return scene;
bool SecondScence::init(){
CCLabelTTF* label = CCLabelTTF::create("hfiejfeiojfoej", "Arial", 30);
label->setPosition(ccp(200,200));
this->addChild(label);
CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png","CloseSelected.png",this,menu_selector(SecondScence::popScene));
pCloseItem->setPosition(ccp(CCDirector::sharedDirector()->getWinSize().width-20, 20));
CCMenu *pMenu = CCMenu::create(pCloseItem,NULL);
pMenu->setPosition(CCPointZero);
this->addChild(pMenu,1);
return true;
}
void SecondScence::popScene(CCObject* pSender){
CCDirector::sharedDirector()->popScene();
}
By the way, I use cocos2dx 2.2 and xcode5, the the console print a message:Cocos2d: cocos2d: deallocing CCDirector 0x6906f0
Verify that the popScene method isn't run twice, perhaps by the user quickly tapping the menu item (or a bug).
That would pop both the current and the HelloWorld scene, leaving the director with no scene to display. It would also explain the director deallocating.
You can prevent this by first checking whether the director's runningScene is identical to this (as in: the SecondScene instance), and only then call popScene.
I have same problem too, but I solved,
I think you may have removed all children from the scene,
check you onExit or destructor to see if any release/remove options available in these two functions.
if the scene has no children, it would be black.
Related
I use this code to show game over menu after _hero sprite and _enemy sprite collide :
if (CGRectIntersectsRect(_hero.boundingBox,_enemy.boundingBox)) {
CCActionCallFunc *_callShowMenu=[CCActionCallFunc actionWithTarget:self selector:#selector(showMenu)];
[self runAction:_callShowMenu];
// EDIT : I also remove both sprites when collision happens.
[_hero removeFromParent];
[_enemy removeFromParent];
}
In _callShowMenu I just stop all actions and show a sprite with half transparent black background image and buttons.
Sometimes when collision happens, it seems to me, that _callShowMenu is called twice, because
background is completely black, like there is the same image behind. Has anyone had a similar problem? (Mostly background image is half-transparent, as it should be).
EDIT:
-(void)showMenu{
[[CCDirector sharedDirector] pause];
CCSprite *_halfTransparentBackground=[CCSprite spriteWithImageNamed:#"halfTransparentBackground.png"];
_halfTransparentBackground.position=ccp(160, 280);
[self addChild:_blackBack z:5];
}
I found a solution using BOOL. Actually everyone uses BOOL in this case, so I don't need to reinvent the wheel.
BOOL doNotCallMeTwice;
Somewhere in the didLoad method:
doNotCallMeTwice=NO;
In the collision detection method:
if (doNotCallMeTwice==NO) {
[self showMenu];
}
And finally:
-(void)showMenu{
doNotCallMeTwice=YES;
}
Possibly, showMenu was called twice(or much more times),because collision detection is in the update method.
I am trying to take a particle effect in my Cocos2d project. Particle effect shows good. But I am confused when I put the particle showing function into a thread as follows, it only shows tiny dark squares instead of the right texture. thanx in advance.
// thread caller
[self performSelectorInBackground:#selector(showParticleThrd:) withObject:nil];
// it works good when i call it directly
-(void) showParticleThrd{
CCParticleSystem * system = [[ParticleEffectSelfMade alloc] initWithTotalParticles:1];
[aLayer removeChildByTag:R_TAG cleanup:YES];
system.position = ccp(self.position.x,self.position.y);
[self.parent addChild:system z:R_ORDER tag:R_TAG];
[system release];
}
You can not modify anything cocos2d related in a background thread. Cocos2d requires you to make changes to nodes in the main thread, where the OpenGL context was created.
I started my app using the base code found here in the menus tutorial. In this manner, all of my 'screens' (there are only 5 of them) are implemented as extensions of the CCLayer class, and I have a shared + Scene Manager which works by adding my layer classes as children to a new scene and then using the director to run or replace the currently playing scene:
+(void) goMenu{
// \/---------- Issue right here, next line:
CCLayer *layer = [MenuLayer node];
[SceneManager go: layer];
}
+(void) go: (CCLayer *) layer{
CCDirector *director = [CCDirector sharedDirector];
CCScene *newScene = [SceneManager wrap:layer];
if ([director runningScene]) {
[director replaceScene: newScene];
}else {
[director runWithScene:newScene];
}
}
+(CCScene *) wrap: (CCLayer *) layer{
CCScene *newScene = [CCScene node];
[newScene addChild: layer];
return newScene;
}
The problem I am having is as follows. Let's say I have 2 layers -- 'MenuLayer' and 'GameLayer'. I start off with MenuLayer and later on use [SceneManager go:[GameLayer node]] to transition over to GameLayer. From GameLayer, if I goMenu the app terminates with an 'NSInternalInconsistencyException', reason: 'child already added. It can't be added again' where indicated. I assumed that this is happening because I am trying to add some child sprites in the layer's init code and I am re-adding them again.
My first attempt at debugging was to call [self removeAllChildrenWithCleanup:YES]; within the onExit code of all my layers. That didn't solve the issue. I added in some debugging logs and found out the last line that executes before the script blows up is the indicated one [myLayerClass node];. The init code inside the layer class does not get executed at all -- so it can't be one of the sprites or other children being added that is causing the issue. Again -- remember that everything works the first time I try to open any scene. It's moving back to an opened scene that is currently problematic.
So I tried a different approach -- the approach used in Cocos2D Hello world. I modified all my Layer Classes by adding a singleton +(id) scene method defined as follows:
+(id) scene
{
CCScene *scene = [CCScene node];
myLayerClass *layer = [myLayerClass node];
[scene addChild: layer];
return scene;
}
Added in a [super dealloc] in the dealloc, and encapsulated all my init code within:
-(id) init
{
if( (self=[super init] )) {
// All init code here....
}
}
Of course, all of the goLayerName singleton methods in the scene manager had their contents replaced to match this -- for example:
+(void) goMenu {
//CCLayer *layer = [MenuLayer node];
//[SceneManager go: layerMenu];
CCDirector *director = [CCDirector sharedDirector];
if ([director runningScene])
[director replaceScene:[MenuLayer scene]];
else
[director runWithScene:[MenuLayer scene]];
}
I figured this was the way Cocos2D's hello world worked, it must be good. Also, since each of my scenes would be created once and once alone (and thus every layer would be created once and once alone). No effect whatsoever! The first time I visit any scene it runs as expected. Whenever I try to navigate back to any existing scene, I get the error I mentioned above.
I tried poking around SO and found this, but I am unsure how to properly implement it; also I'm not entirely sure that would even solve the issue -- since the symptoms are different (he just gets a pink screen, whereas my app terminates).
Any assistance would be appreciated.
For anyone who was interested in this question, I was able to resolve the issue.
In summary, any child of a child you add, will not be released with releaseAllChildrenWithClenup. That function will only release the immediate children, so you are responsible to release any nested children yourself.
In my game, I had some characters and their eyes were nested in the head sprites (so that I could just move / rotate the heads). The eyes had their own animation loop but I wanted them to transform with the head. All that worked, but since I had a line of code in init which basically [headSprite addChild:eyeSprite];, I had to also add [headSprite removeChild:eyeSprite]; in the onExit code to dissociate it, followed by releaseAllChildrenWithClenup to remove all of the n=1 level children as well.
Once that was handled, everything worked as advertised.
my problem is:
I'm making a game for iOS using cocos2d and this game has lots of levels, so I'll have to create a loading scene to load my sprites for each level. ( like new backgrounds, monsters and other stuff )
But I have no idea about this, I'm adding all the Texture Packer Files (.plist and .pvr.ccz)
on the sharedSpriteFrameCache in the GameData.m.
Does anyone knows a good tutorial for this or can help me solve this?
Thanks!
So basically you want to know how to load and unload images as you see fit. How about
#implementation Level1
- (void) loadLevel
{
CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCTextureCache* textureCache = [CCTextureCache sharedTextureCache];
// Add the sprite frames. This will load the texture as well
[frameCache addSpriteFramesWithFile:#"monkey.plist"];
[frameCache addSpriteFramesWithFile:#"player.plist"];
[frameCache addSpriteFramesWithFile:#"enemy.plist"];
// Load other textures that are going to be used
_myBackgroundTexture = [textureCache addImage:#"background.png"];
}
- (void) unloadLevel
{
CCSpriteFrameCache* frameCache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCTextureCache* textureCache = [CCTextureCache sharedTextureCache];
// Remove textures
[textureCache removeTexture:_myBackgroundTexture];
// Remove sprite frames. This will load the texture as well
[frameCache removeSpriteFramesFromFile:#"monkey.plist"];
[frameCache removeSpriteFramesFromFile:#"player.plist"];
[frameCache removeSpriteFramesFromFile:#"enemy.plist"];
// Though normally, id use frameCache removeUnusedSpriteFrames and
// textureCache removeUnusedTextures
}
...
#end
You can use the sprite by CCMenuItem and also by the Menu as you would require to click and move to that particular level.. The below is the code for adding the menu item image
CCMenuItem *m4 =[CCMenuItemImage itemFromNormalSprite:[CCSprite spriteWithSpriteFrameName:<#(NSString *)spriteFrameName#>]
selectedSprite:[CCSprite spriteWithSpriteFrameName:<#(NSString *)spriteFrameName#>]
disabledSprite:[CCSprite spriteWithSpriteFrameName:<#(NSString *)spriteFrameName#>]
target:self selector:#selector(MoveLeft)];
The above code gives the information you can use to display the sprite for the particular state of the menu. And the "MoveLeft" is the method selector which I have used to call the particular method.
At last you can add the m4 object to the CCMenu and get the desired output...
Hope it works for you.
I want to set the contentsize of scrollayer.
I have a scrollayer, it's CCLayer type and moving is set by ccTouchMove. I have one schedule for smoothing. BUT.
Problem is that scrolling layer is big like the whole display. I want to set the contentsize of scrollayer. Content in this layer will be scroll and showing ONLY in this layer. Not taking up the whole display. Something like this
Scrolling just in gray CCLayer (scrollayer) ....NOT WHOLE SCREEN.
Can you help me?
P.S.: Setting CCLayerColor and initWithColor:Width:Height: is not working. It just makes some stupid color box and it's moving too.
ok, honestly i would put the window frame at a higher z than the scrolling object ... if you dont you may have to crop and change sprite on the fly for the window content, nasty (at least that is the one way i could do this, without further research).
so :
// initialize this logic somewhere useful
CCNode scrollableContent;
CCSprite windowFrame;
BOOL isScrollPossible;
[self addChild:scrollableContent z:0];
[self addChild:windowFrame z:1];
// and in the touch delegate methods
-(void) ccTouchBegan:{
check if the touch happened in the window, if yes, scrolling is possible
}
-(void) ccTouchMoved:{
if (isScrollPossible) {
compute displacement
compute nextPosition for scrollableContent node;
if ( nextPosition in window ) {
// make scrollableContent follow touch
scrollableContent.position=nextPosition;
} else {
// stop any further scrolling until the next 'touch began'
isScrollPossible=NO;
}
} else {
// scroll not possible, do nothing
}
}
This is the basic idea. You may need clamping logic to prevent the creeping of scrollableContent beyond the edges of the window.
Edited for typos.
After trying desperately with masking and GL_SCISSOR, I settled on cutting a hole in the foreground and only moving the background object when onTouchMoved updated.
To see it in action, have a look at the Stats page of Angry Gran.