There is a scene in my application which has only two labels and a menu item. When I load this scene using replaceScene method it stays for 3-4 seconds and then gets disappeared or released. I want to keep it until cancel button is pressed. How can I do it? code is:
#implementation MyLayer
+ (id)myScene {
CCScene *aScene = [CCScene node];
MYLayer *myLayer = [MyLayer node];
[aScene addChild:myLayer];
return aScene;
}
- (id) init {
if (self = [super init]) {
//labels and menu here
}
return self;
}
And I am calling it from another scene like this:
[[CCDirector sharedDirector] replaceScene: [MyLayer myScene]];
Maybe the problem is that it's your first scene. Then you should use runWithScene method of CCDirector.
did you try replacing that scene with a "empty" init function to see if it still releases itself? It might be because of the amount of textures you are putting into memory
I did have sort of similar problems before because the images used in the new scene is too big and got auto purged by my app delegate, thus returning me an empty scene sometimes
Related
I push a preloaded scene B onto scene A like this. The root node in the scene B has an animation that runs for ~2 seconds, and I want scene B to be popped when the animation is done. I think it should work like below. However it crashes on popScene. Does push/pop really work in Cocos2D v3?
SceneRedroom* sceneredroom = (SceneRedroom*)[self.ccscenewithSceneredroom getChildByName:#"SceneRedroom" recursively:NO];
[sceneredroom.animationManager setCompletedAnimationCallbackBlock:^(id sender) {
[[CCDirector sharedDirector] popScene];
}];
[[CCDirector sharedDirector] pushScene:self.ccscenewithSceneredroom];
The crashes I get are fairly random. It seems like some things are incorrectly deallocated or similar when pushing and/or popping scenes.
After further research, I managed to find a solution to the problem I was having through this post. I removed the CCDirector pause and resume in my pauseGame and resumeGame methods then added this:
//pause
[sprite.actionManager pauseTarget:sprite];
//resume
[sprite.actionManager resumeTarget:sprite];
This stopped the animation and kept the "paused" state even if the game is closed then reopened and I didn't even need to play with the AppDelegate class :). I hope this helps others too.
UPDATE: Just in case anybody else is creating a sprite using a loop, here's how I managed to create the pause and resume function for it:
[sprite.actionManager pauseAllRunningActions];
[sprite.actionManager resumeTargets:[NSSet setWithArray:spriteArray]];
note the difference between the two (resumeTarget and resumeTargets) then since resumeTargets would ask for NSSet, I simply passed the array objects into a NSSet with the above code.
void PauseScene::goToMainMenuScene(cocos2d::Ref *sender)
{
Director::getInstance()->resume();
auto scene = MainMenuScene::createScene();
Director::getInstance()->replaceScene(TransitionFade::create(TRANSITION_TIME, scene));
}
void PauseScene::resumeScene(cocos2d::Ref *sender)
{
Director::getInstance()->popScene();
}
void PauseScene::restartScene(cocos2d::Ref *sender)
{
Director::getInstance()->resume();
auto scene = GameScene::createScene();
Director::getInstance()->replaceScene(TransitionFade::create(TRANSITION_TIME, scene));
}
When I do this:
[gameLayer pauseSchedulerAndActions];
Most of the game pauses, but the sprites that are undergoing this action do not pause spinning:
[sprite runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:5.0 angle: 360]]];
Also, those sprites that are running CCAnimations do not stop animating:
CCAnimation *theAnim = [CCAnimation animationWithSpriteFrames:theFrames delay:0.1];
CCSprite *theOverlay = [CCSprite spriteWithSpriteFrameName:#"whatever.png"];
self.theAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:theAnim]];
How can I get these to pause when the game is paused? I would expect “pauseSchedulerAndActions” to pause actions, but that doesn’t seem to be the case.
pauseSchedulerAndActions is not recursive, so it will only affect actions and schedules on the node you are pausing, not it's children.
if your scene/layer is shallow, you could probably just get away with looping through the layer's children and calling (pause/resume)SchedulerAndActions on each, otherwise, if you have a deeper graph, you'll probably want to call it recursively. I wrote up a small test and this worked for me:
-(void) pauseSchedulerAndActions: (BOOL) pause forNodeTree:(id)parentNode shouldIncludeParentNode:(BOOL)includeParent
{
if(includeParent)
{
(pause) ? [parentNode pauseSchedulerAndActions] : [parentNode resumeSchedulerAndActions];
}
for( CCNode *cnode in [parentNode children] )
{
(pause) ? [cnode pauseSchedulerAndActions] : [cnode resumeSchedulerAndActions];
if(cnode.children.count > 0)
{
[self pauseSchedulerAndActions:pause forNodeTree:cnode shouldIncludeParentNode:NO]; // on recurse, don't process parent again
}
}
}
so in your case you could try calling this method and passing in your gameLayer
Try [[CCDirector sharedDirector] pause]; It should pause all animations and movements.
I currently have a "Console" CClayer, which is handling touch detection for sprites that have been added to it. However, I also have some sprites that I want to do touch detection on that are not part of the Console layer... They are currently children of a class that inherits from CCNode.
My understanding is, the more cocos objects have the "isTouchEnabled" property set to true, the more performance will be affected, so I am curious how I should approach this?
Should I:
A) Have the console's touchesBegan method perform detection of the sprites belonging to the CCNode?
B) Just implement isTouchEnabled on the CCNode object
C) Some other approach?
well, for starters, you should only concern yourself about performance if you are concerned i.e. you are seeing or measuring (on DEVICES not a simulator) some inappropriate response times.
I would avoid detecting touches that concern another node - it could get messy, software wise. I tend to return YES (from a ccTouchBegan) strictly when the touch is at a location of an object of concern to the detecting node. When you return NO, the dispatcher will pass on the touch to other handlers ('under' the console), until one such CCNode takes the bite. Kind of as follows:
- (void) onEnter{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[super onEnter];
}
- (void) onExit{
[[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
if (!_visible || !_enabled) {
return NO;
}
CGPoint loc = [touch locationInView:touch.view];
loc = [[CCDirector sharedDirector] convertToGL:loc];
if ([self containsPoint:loc]) {
// do your thing here !
return YES;
}
return NO;
}
-(BOOL) containsPoint:(CGPoint) location {
// determine here whether this node should be handling
// this touch.
}
I am using tilemap in my box2D game in which I have created powers. When the player hits the power, a score label is displayed on the screen. Along with this I want to remove that power from the tilemap when player hits it. I have displayed label but I am unable to remove the power. Here is some code :
In ContactListener I am calling the method which removes the power from tilemap :
void ContactListener::BeginContact(b2Contact *contact) {
else if(actorA.tag==obj.gamePower.tag) //obj is a DataClass object.
{
[GameScene addPointLabel]; // For displaying score label
[GameScene removePower:actorA];
}
+(void)removePower:(id)sender
{
GameScene *obj=[[GameScene alloc]init];
CCSprite *sprite = (CCSprite *)sender;
[obj removePowerFromScene:sprite];
[obj release];
}
-(void)removePowerFromScene:(id)sender
{
CCSprite *sprite = (CCSprite *)sender;
[self removeChild:sprite cleanup:YES];
}
I have created an object layer on tilemap to display power. But somehow, I am unable to remove it. Can someone help me?
If power is a CCNode why don't you remove it with [power removeFromParentAndCleanup:YES] ?
The (removePower:) does remove nothing because it creates a new scene and remove the sprite from that scene where the sprite does not belong to.
Another notice, be careful with contact listener. Removing o node in BeginContact is potential of crash. Let imagine the case that powerA contacts with both actor1 and actor2. The first call to BeginContact with powerA and actor1 removes powerA, so subsequent call to BeginContact with powerA envolved will crash !
I am trying to preload an animation in the init method of my layer. I then call the animation if the screen is touched. The app crashes with no error message as soon as I touch the screen and seems it is to do with calling the preloaded animation. I would like to do it this way as it seems expensive to create the animation every time the screen is touched - which does seems to work though. Any tips greatly appreciated.
Sample Code:
In my header:
#interface Test : CCLayer {
NSMutableArray *wake;
CCSprite* ani;
CCAnimate *animate;
}
#end
In my implementation:
-(id) init {
if( (self=[super init])) {
// enable touches
self.isTouchEnabled = YES;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"ani.plist" texture:[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:#"ani.png"]]];
ani = [CCSprite spriteWithSpriteFrameName:#"ani1.png"]; //comes from .plist file
ani.anchorPoint=ccp(0,0);
ani.position = ccp(700,65);
[self addChild:ani z:30];
wake = [NSMutableArray array];
for(int i = 1; i <= 4; i++) {
[wake addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"ani%d.png",i]]];
}
animate = [CCAnimate actionWithAnimation:[CCAnimation animationWithFrames:wake delay:1.0f] restoreOriginalFrame:FALSE];
}
return self;
}
Handling the touch:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// run the animation
[ani runAction:animate];
}
Animations in Cocos2d are not designed to be reused. You need to create a new one every time.
Problem solved by creating properties for the array and animation on the class using nonatomic,retain.
You only need to retain the animation but the array can be local.
self.myAnimation = [[CCAnimation animationWithFrames:myAniFramesArray delay:0.1f] retain];
Remember to make the property nonatomic, retain as stated by Chev and to release any objects you retain in the appropriate dealloc method.