Cocos2d Game crashing when replacing scene (Code Included) - iphone

so something is going haywire with my Cocos2d Game. I recently added in some code that would allow me to have a dialog message at the beginning of certain levels in my game. And whenever I go to restart a level (Which replaces the scene) of a level that has the dialog cclayer added to the current scene, I get a BAD ACCESS error through the iPhone simulator, and then if I run it on my iPhone 4S I'm getting a SIGARBT error that says
"* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'child already added. It can't be added again'"
This is my first cocos2d game I'm making. I've toyed a lot with cocos2d and have what I believe is a basic understanding. I have a lot of programming experience with other languages, but I only started learning objective-c about 3 months ago.
Here is my code.
GameDialog.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "GameData.h"
#interface GameDialogLayer : CCLayer {
NSString *dialogText;
}
#property (nonatomic, retain) NSString *dialogText;
+(CCScene *) scene;
-(void) addDialogWithText: (NSString *)text;
#end
GameDialog.m
#import "GameDialogLayer.h"
#implementation GameDialogLayer
#synthesize dialogText;
+(CCScene *) scene
{
CCScene *scene = [CCScene node];
GameDialogLayer *layer = [GameDialogLayer node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if( (self=[super init]) ) {
// init the bg overlay
CCLayerColor *backgroundLayer = [CCLayerColor layerWithColor:ccc4(0, 0, 0, 175)];
[self addChild:backgroundLayer z:0];
// init the dialog box
CCSprite *dialogBox = [CCSprite spriteWithFile:#"dialog.png"];
dialogBox.position = ccp([[CCDirector sharedDirector] winSize].width / 2, [[CCDirector sharedDirector] winSize].height / 2);
[self addChild:dialogBox];
// init the dialog menu
CCMenuItemImage *dialogOkay = [CCMenuItemImage itemWithNormalImage:#"dialogOkayButton.png" selectedImage:#"dialogOkayButtonPressed.png" target:self selector:#selector(okayButton)];
CCMenu *dialogMenu = [CCMenu menuWithItems:dialogOkay, nil];
dialogMenu.position = ccp(dialogMenu.position.x, dialogMenu.position.y - 118);
[self addChild:dialogMenu];
}
return self;
}
-(void) okayButton
{
[GameData sharedGameData].isPaused = 0;
[[self parent] schedule:#selector(startCountdown:) interval:1];
[[self parent] removeChild:self cleanup:YES];
}
-(void) addDialogWithText: (NSString *)text
{
CCLabelTTF *dialogTextLabel = [CCLabelTTF labelWithString:text dimensions:CGSizeMake(180, 250) hAlignment:kCCTextAlignmentLeft fontName:#"MarkerFelt-Thin" fontSize:20];
dialogTextLabel.color = ccBLACK;
dialogTextLabel.position = ccp([[CCDirector sharedDirector] winSize].width / 2, [[CCDirector sharedDirector] winSize].height / 2);
[self addChild:dialogTextLabel];
}
- (void) dealloc
{
[super dealloc];
}
#end
And then the dialog is being added to my dialog property in my levelData class that is istantiated for each level and holds the properties of the level.
Here is the property holding it
#property (nonatomic, retain) GameDialogLayer *dialog;
And here is how I'm adding the dialog to the levelData dialog property
-(void) addDialog: (NSString *)dialogText
{
dialog = [GameDialogLayer node];
[dialog addDialogWithText:dialogText];
}

You are adding a node to the scene twice somehow, I had a look at your code and couldn't spot it but they are easy to find. Just add a breakpoint for all Objective C exceptions. The debugger will stop on the line causing the problem.
See here for how to add the breakpoint. You can change "Exception: All" to "Exception: Objective C" to avoid exceptions from CocosDenhesion if you are using it.

Related

How to access sprites from a different class

I'm trying to make an app and I am currently trying to separate data into different files.
I will give some code from the main class (ArcherClass) and the player class.
This is the player class:
Player.h______________________
#interface Player:CCLayer{
CCSprite *_head;
}
#property (nonatomic, retain) CCSprite *head;
-(void)setHead:(CCSprite *)head;
+(id)player;
#end
Player.m______________________
#implementation Player
#synthesize head = _head;
+(id)player{
Player *playerSprite = nil;
return playerSprite;
}
-(void)setHead:(CCSprite *)head{
_head = [CCSprite spriteWithFile:#"head.png"];
}
#end
ArcherClass.m______________________
#import "ArcherClass.h"
#import "cocos2d.h"
#import "Player.h"
#implementation ArcherClass
//skip some stuff...
+(id) scene{
CCScene *scene = [CCscene node];
CCLayer* layer = [ArcherClass node];
[scene addChild:layer];
return scene;
}
-(id) init{
if(self = [super init]{
self.isTouchEnabled = YES;
}
Player *player = [[Player alloc]init];
CCSprite *bob = player.head;
bob.position = ccp(250,250);
//There are layers in this class, so this may be a problem
}
I had the app up and working, everything worked previously because everything was local to the ArcherClass. There were no other classes. Now I am trying to make seperate classes for everything, but I am running into problems. How can I fix this?

How to deallocate a CCLayer

I am making a box2d app for the iphone using cocos2d. I am trying to switch my CCLayer going from my HelloWorldLayer to my HomeScene and I get an error "Thread 1: Program received signal: "EXC_BAD_ACCESS"." It gives me that error when I try to call [super dealloc] in my dealloc method for my HelloWorldLayer. Please Help. Here is my .h and .mm
#interface HelloWorldLayer : CCLayer
{
b2World *world;
Cannon *cannon1;
Cannon *cannon2;
Cannonball *cannonball1;
Cannonball *cannonball2;
float theMass;
float theMass2;
CCSprite *sunBack;
b2Vec2 cannon1Pos;
b2Vec2 cannon2Pos;
CCMenuItemSprite *pauseBut;
CCMenuItemSprite *playBut;
CCMenu *pauseMenu;
}
#property(nonatomic)b2Vec2 cannon1Pos;
#property(nonatomic)b2Vec2 cannon2Pos;
#property(nonatomic, retain)CCSprite *sunBack;
#property(nonatomic, retain)Cannon *cannon1;
#property(nonatomic, retain)Cannon *cannon2;
#property(nonatomic, retain)Cannonball *cannonball1;
#property(nonatomic, retain)Cannonball *cannonball2;
#property(nonatomic, retain)CCMenu *pauseMenu;
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
+(HelloWorldLayer *) sharedLayer;
-(void)createMonkeys;
-(void)restartGame;
-(void)playGame;
-(void)pauseGame;
-(CCSpriteBatchNode*)getSpriteBatch;
-(void)goToHome;
#end
Here is my .mm where I deallocate
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
delete world;
world = NULL;
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[cannon1 removeFromParentAndCleanup:YES];
[cannon2 removeFromParentAndCleanup:YES];
[cannonball1 removeFromParentAndCleanup:YES];
[cannonball2 removeFromParentAndCleanup:YES];
// don't forget to call "super dealloc"
[super dealloc];
}
You can try this one instead of the dealloc you have used
-(void) dealloc
{
delete world;
world = NULL;
cannon1 = NULL;
cannon2 = NULL;
cannonball1 = NULL;
cannonball2 = NULL;
[super dealloc];
}

Cocos2D: Subclassing sprite and animation as CCLayer is giving me trouble

I started experimenting with Cocos2D with Tiled, and had the player sprite and actions coded within a CCLayer along with everything else. Before continuing on, I wanted to subclass the player into a CCLayer, which I hope is correct.
My header and main code is as follows:
HeroClass.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface HeroClass : CCLayer {
CCSprite *_hero;
CCAction *_heroSpriteFlyAction;
}
#property(nonatomic, retain) CCSprite *hero;
#property(nonatomic, retain) CCAction *heroSpriteFlyAction;
#end
HeroClass.m
#import "HeroClass.h"
#implementation HeroClass
#synthesize hero =_hero;
#synthesize heroSpriteFlyAction = _heroSpriteFlyAction;
-(id) init{
self = [super init];
if (!self) {
return nil;
}
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"heroTestSheet.plist"];
CCSpriteBatchNode *heroSpriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"heroTestSheet.png"];
[self addChild:heroSpriteSheet];
NSMutableArray *heroSpriteFlyAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 2; ++i) {
[heroSpriteFlyAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"heroFrame%d.png", i]]];
}
CCAnimation *heroSpriteFlyAnim = [CCAnimation animationWithFrames:heroSpriteFlyAnimFrames delay:0.03f];
self = [CCSprite spriteWithSpriteFrameName:#"heroFrame1.png"];
_heroSpriteFlyAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:heroSpriteFlyAnim restoreOriginalFrame:NO]];
[self runAction:_heroSpriteFlyAction];
[heroSpriteSheet addChild:self];
return self;
}
- (void) dealloc{
self.hero = nil;
self.heroSpriteFlyAction = nil;
[super dealloc];
}
#end
I think the idea I want to achieve is that I can access things in this class as properties in other files. The code above gives no errors when I build it, but maybe I didn't do something right. The problem I'm having with the migration is what is happening now in my CCLayer class DebugZoneLayer, which creates the map and is supposed to add my player sprite but is giving me errors.
In DebugZoneLayer.h I imported the HeroClass.h and made a pointer from the HeroClass of the hero sprite and gave it a property. No errors here but it may be the start of where I'm going wrong:
#import "cocos2d.h"
#import "HeroClass.h"
#class HeroClass;
// DebugZone Layer
#interface DebugZoneLayer : CCLayer {
HeroControl *heroControl;
HeroClass *hero;
CCTMXTiledMap *theMap;
CCTMXLayer *blocksCollidable;
CCTMXLayer *invisiblePropertiesLayer;
}
#property(nonatomic, retain) CCSprite *hero;
In DebugZoneLayer.m, when I synthesize hero, it gives the error "Type of property 'hero' does not match type of ivar 'hero'
#synthesize hero;
The rest of the file gives me more errors related to anything referencing hero, but at least that's where it starts.
EDIT (updated)
Just wanted to mention, since this was solved I cleared up some major issues in HeroClass.m which was causing a crash:
#import "HeroClass.h"
#implementation HeroClass
#synthesize heroSprite =_heroSprite;
#synthesize heroSpriteSheet =_heroSpriteSheet;
#synthesize heroSpriteFlyAction = _heroSpriteFlyAction;
-(id) init{
self = [super init];
if (!self) {
return nil;
}
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"heroTestSheet.plist"];
_heroSpriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"heroTestSheet.png"];
//[self addChild:_heroSpriteSheet];
NSMutableArray *heroSpriteFlyAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 2; ++i) {
[heroSpriteFlyAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"heroFrame%d.png", i]]];
}
CCAnimation *heroSpriteFlyAnim = [CCAnimation animationWithFrames:heroSpriteFlyAnimFrames delay:0.03f];
_heroSprite = [CCSprite spriteWithSpriteFrameName:#"heroFrame1.png"];
_heroSpriteFlyAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:heroSpriteFlyAnim restoreOriginalFrame:NO]];
[self runAction:_heroSpriteFlyAction];
[_heroSpriteSheet addChild:_heroSprite];
return self;
}
- (void) dealloc{
self.heroSprite = nil;
self.heroSpriteSheet = nil;
self.heroSpriteFlyAction = nil;
[super dealloc];
}
#end
This is not 100% related to your problem .. but you have another issues with your properties.
You define your property as retain, and you release it in the dealloc function, but actually you never retain the object.
_heroSprite = [CCSprite spriteWithSpriteFrameName:#"heroFrame1.png"];
at this position the _heroSprite variable contains the sprite with autorelease enabled... you don't retain it.
Of course you don't loose it, because it will get retained by this line:
[heroSpriteSheet addChild:_heroSprite];
but it will get released when the child is removed from the sheet.
so this is unnecessary in dealloc: self.heroSprite = nil; and [_heroSprite release]; would even crash your code.
As said before, the code works, but when you look over it later, you might get confused.
You should declare the proberty as (nonatomic, assign) or retain it properly with
self.herosprite = [CCSprite spriteWithSpriteFrameName:#"heroFrame1.png"];
Trying changing your property in the DebugZoneLayer class from:
#property(nonatomic, retain) CCSprite *hero;
To:
#property(nonatomic, retain) HeroClass *hero;

Pass level time to Congratulations scene cocos2d

Just wondered if someone could quickly give me a hand. Im building a simple puzzle slider game, ive added a timer to the game now and all i want to do is pass the time it took to complete the puzzle over to the congrats scene. Could anyone just have a look at my code see where im going wrong please?
play.h
#import "cocos2d.h"
#import "Box.h"
#interface PlayLayer : CCLayer
{
int timeInt;
int secs;
int mins;
CCLabel *timeLabel;
NSString *TotalTimeString;
}
#property (nonatomic, assign) int timeInt;
#property (nonatomic, assign) int secs;
#property (nonatomic, assign) int mins;
#property (nonatomic, retain) NSString *TotalTimeString;
#end
Play.m
#import "PlayLayer.h"
#import "Congrats.h"
#implementation PlayLayer
#synthesize timeInt;
#synthesize secs;
#synthesize mins;
#synthesize TotalTimeString;
-(id) init{
self = [super init];
TotalTimeString = [NSString stringWithFormat:#"Time: %02d:%02d", mins, secs];
timeLabel = [[CCLabel labelWithString:TotalTimeString dimensions: CGSizeMake(130,27) alignment: UITextAlignmentCenter fontName:#"Marker Felt" fontSize:25.0] retain];
timeLabel.position = ccp(155,430);
[self addChild:timeLabel];
[self schedule: #selector(tick2:) interval:1.0];
return self;
}
-(void) tick2: (id) sender{
timeInt++;
secs = timeInt % 60;
mins = timeInt / 60;
[timeLabel setString: TotalTimeString];
}
-(void) check: (id) sender data: (id) data{
int valueToCheck = 0;
bool breakOut = false;
for (int y=0; y < box.size.height && !breakOut; y++) {
for (int x=0; x < box.size.width && !breakOut; x++) {
Tile *tile = [box objectAtX:x Y:y];
if (tile.check != [box hashOfXY:x y:y]) breakOut = true;
valueToCheck++;
}
}
int totalTiles = box.size.width * box.size.height;
if (valueToCheck == totalTiles){
[[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:1 scene:[Congrats node]]];
CCScene *scene = [Congrats scene];
scene.TotalTimeTakenWhenDiedString = TotalTimeString;
[[CCDirector sharedDirector] replaceScene:scene];
}
}
#end
Congrats.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "PlayLayer.h"
#interface Congrats : CCLayer {
CCLabel *timeLabel;
NSString *TotalTimeTakenWhenDiedString;
}
#property (nonatomic, assign) NSString *TotalTimeTakenWhenDiedString;
+(id) scene;
#end
Congrats.m
#import "Congrats.h"
#import "DifficultyLevel.h"
#import "PlayLayer.h"
#import "PlayLayerMedium.h"
#import "PlayLayerHard.h"
#import "MainMenu.h"
#implementation Congrats
#synthesize TotalTimeTakenWhenDiedString;
+(id) scene {
// ‘scene’ is an autorelease object.
CCScene *scene = [CCScene node];
// ‘layer’ is an autorelease object.
Congrats *layer = [Congrats node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
// on “init” you need to initialize your instance
-(id) init {
// always call “super” init
// Apple recommends to re-assign “self” with the “super” return value
if( (self=[super init] )) {
timeLabel = [[CCLabel labelWithString: TotalTimeTakenWhenDiedString dimensions: CGSizeMake(130,27) alignment: UITextAlignmentCenter fontName:#"Marker Felt" fontSize:25.0] retain];
timeLabel.position = ccp(155,430);
}
return self;
}
-(void) goToGameplay: (id) sender {
[[CCDirector sharedDirector] replaceScene:[CCFadeTransition transitionWithDuration:1 scene:[MainMenu node]]];
}
}
#end
I was kind of hoping that, that would work. But i keep getting this error "error: request for member 'TotalTimeTakenWhenDiedString' in something not a structure or union"
Anyone help at all?
Cheers
Your problem is here:
CCScene *scene = [Congrats scene];
scene.TotalTimeTakenWhenDiedString = TotalTimeString;
The object scene is of type CCScene and you are trying to access TotalTimeTakenWhenDiedString on it. But that is a property of Congrats, not of CCScene.
Here's a suggestion which might work, by getting the Congrats object out from scene. (Caveat: I'm not familiar with cocos2d, and I only looked it up quickly here. This may be a terrible way to be doing it!)
Change here:
// add layer as a child to scene
[scene addChild:layer z:0 tag:1]; // tag it with 1
and here:
CCScene *scene = [Congrats scene];
Congrats *congrats = (Congrats *)[scene getChildByTag:1]; // get the congrats child object, which we tagged 1
congrats.TotalTimeTakenWhenDiedString = TotalTimeString;
[[CCDirector sharedDirector] replaceScene:scene];
... Why dont you just create a singleton class which you can use as a global object? and access/update it anywhere in the game?

Coco2d - using CCBitmapFontAtlas

i'm trying to create a simple game with COCO2d but no luck so far...
when i'm trying to create a CCBitmapFontAtlas i get a error saying :
"_OBJC_CLASS_$_CCBitmapFontAtlas", referenced from:"
and also :
"'CCBitmapFontAtlas' is deprecated "
here is my header file:
#interface MainMenuScene : CCLayer
{
CCBitmapFontAtlas* startNewGameLabel;
}
(id) scene;
#end
and here is my implementation file :
#import "MainMenuScene.h"
#implementation MainMenuScene
(id) scene
{
CCScene scene = [CCScene node];
CCLayer layer = [MainMenuScene node];
[scene addChild:layer];
return scene;
}
-(id) init
{
if ((self = [super init]))
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
[self setVisible:YES];
startNewGameLabel = [CCBitmapFontAtlas
bitmapFontAtlasWithString:#"New Game"
fntFile:#"bitmapfont.fnt"];
//[CCLabelTTF labelWithString:#"New Game"
// fontName:#"AppleGothic"
// fontSize:48];
CGSize size = [[CCDirector sharedDirector] winSize];
startNewGameLabel.position = CGPointMake(size.width / 2, size.height / 2);
[self addChild:startNewGameLabel];
}
return self;
}
(void) dealloc
{
CCLOG(#"%#: %#", NSStringFromSelector(_cmd), self);
[super dealloc];
}
#end
I created both .FNT file and .PNG file with heir
You want CCLabelBMFont instead of CCBitmapFontAtlas.
startNewGameLabel = [CCLabelBMFont
labelWithString:#"New Game"
fntFile:#"bitmapfont.fnt"];
Use CCLabelBMFont instead. Will be removed 1.0.1