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

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;

Related

separate class for drawing sprites, class doesn't work

i'm working on some sample game using cocos2d to get more practise and I have a problem with classes. Here's my example:
someShapes.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface palleteOfShapes : CCLayer
{
NSMutableArray *shapesArray;
}
#property (nonatomic,retain) NSMutableArray *shapesArray;
-(void)drawPallete;
#end
someShapes.m
#import "palleteOfShapes.h"
#implementation palleteOfShapes
#synthesize shapesArray;
-(void)drawPallete
{
shapesArray = [NSMutableArray arrayWithObjects:#"Icon.png",#"A.png",#"questionMark.png",nil];
for (int i=0; i<shapesArray.count; i++) {
NSString *imagestring = [shapesArray objectAtIndex:i];
CCSprite *sprite = [CCSprite spriteWithFile:imagestring];
NSLog(#"i value: %i",i);
sprite.position=ccp(100*i,350);
NSLog(#"image added:%#",imagestring);
[self addChild:sprite];
NSLog(#"count: %d",[shapesArray count]);
}
NSLog(#"pallete was completed");
[shapesArray removeLastObject];
NSLog(#"count:%d",[shapesArray count]);
}
#end
in the main layer I do:
palleteOfShapes *newPallete = [[palleteOfShapes alloc]init];
[newPallete drawPallete];
I expected that these sprites will appear on my main layer, but they don't.
NSLog shows all messages but no sprites.
So if you can, please tell me what's wrong.
thanks in advance.
You add sprites to "palleteOfShapes" layer, but you never add them or "palleteOfShapes" to main layer.
Implement -(id) init method and try some initialisation there.

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 project crashes when calling method in Init:

I'm trying to experiment with cocos2d by building a simple card game. I'm not new to iOS development but I am very new to the cocos2d engine. I have 2 custom classes so far:
A card class:
.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface Card : NSObject {
CCSprite *faceSprite;
NSString *suit;
NSUInteger value;
float priority;
}
#property (nonatomic, retain) CCSprite *faceSprite;
#property (nonatomic, readonly) NSString *suit;
#property (nonatomic, readonly) NSUInteger value;
#property (nonatomic, assign) float priority;
- (id)initWithSprite:(CCSprite *)paramSprite suit:(NSString *)paramSuit andValue:(NSUInteger)paramValue;
#end
.m
#import "Card.h"
#implementation Card
#synthesize faceSprite, suit, value, priority;
- (id)init
{
self = [self initWithSprite:nil suit:nil andValue:nil];
if (self) {
// Initialization code here.
}
return self;
}
-(id)initWithSprite:(CCSprite *)paramSprite suit:(NSString *)paramSuit andValue:(NSUInteger)paramValue {
if ((self = [super init])) {
faceSprite = paramSprite;
suit = paramSuit;
value = paramValue;
}
return self;
}
#end
And a Deck class:
.h
#import <Foundation/Foundation.h>
#import "Card.h"
#interface Deck : NSObject {
NSMutableArray *cardsArray;
}
#property (nonatomic, retain) NSMutableArray *cardsArray;
-(void)shuffle;
-(Card *)drawTopCard;
#end
.m
- (id)init
{
if ((self = [super init])) {
cardsArray = [[NSMutableArray alloc] init];
for (NSInteger suit = 1; suit < 5; suit++) {
NSString *suitString;
switch (suit) {
case 1:
suitString = #"h";
break;
case 2:
suitString = #"d";
break;
case 3:
suitString = #"s";
break;
case 4:
suitString = #"c";
break;
}
for (NSInteger i = 3; i < 14; i++) {
CCSprite *cardImage = [CCSprite spriteWithFile:[NSString stringWithFormat:#"%d%#.gif",i, suitString]];
Card *card = [[Card alloc] initWithSprite:cardImage suit:suitString andValue:i];
[cardsArray addObject:card];
}
}
}
return self;
}
-(void)shuffle {
int timesToShuffle = 1;
while (++timesToShuffle < 4) {
for (int i = 0; i < [cardsArray count]; i++) {
int cardToSwap = arc4random() % [cardsArray count];
[cardsArray exchangeObjectAtIndex:i withObjectAtIndex:cardToSwap];
}
}
}
-(Card *)drawTopCard {
Card *cardDrawn = [[cardsArray objectAtIndex:0] retain];
//[cardsArray removeObjectAtIndex:0];
return cardDrawn;
}
#end
When I try to do the following in my main scene, I get a EXC_BAD_ACCESS error inside CCScheduler.m in the update:(ccTime) method
-(id) init
{
if( (self=[super init])) {
deck = [[Deck alloc] init];
[deck shuffle];
[self schedule:#selector(drawAndShowCard) interval:3.0];
}
return self;
}
-(void)drawAndShowCard {
// ask director the the window size
CGSize size = [[CCDirector sharedDirector] winSize];
// position the label on the center of the screen
Card *card = [deck drawTopCard];
CCSprite *cardSprite = (CCSprite *)card.faceSprite;
cardSprite.position = ccp( size.width /2 , size.height/2 );
[self addChild:cardSprite];
}
strange thing is if I move the deck alloc init and the shuffle inside the drawCardAndShow method, it no longer crashes, however this is not the correct place for this for OBVIOUS reasons as I don't want a whole new deck every time I draw a card...
any help?
I see one serious problem but you have multiple issues with your code.
When you have strings as properties you should declare them as copy, in your case you have it as assign:
#property (nonatomic, readonly) NSString *suit;
If you want to declare the property readonly, you can once again declare the property inside your .m to make it writeable internally:
#interface Card()
#property (nonatomic, copy)
NSString *suit;
#end
You should use the self. notation for properties to make your intent clear to others when you are accessing the ivar and when accessing the property.
You got a mem leak here:
Card *card = [[Card alloc] initWithSprite:cardImage suit:suitString andValue:i];
[cardsArray addObject:card];
-->missing [card release]
This here is unnecessary
#interface Deck : NSObject {
NSMutableArray *cardsArray;
}
#property (nonatomic, retain) NSMutableArray *cardsArray;
You can omit the ivar, just make sure you do not alloc/init without autorelease.
If you insist on having an ivar rename at least to _cardsArray and then
#synthesize cardsArray=_cardsArray
so that you can distinguish between the two. In one case you need to explicit to retain, in the other you use the setter/getter.
hth
you should set deck as a property

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?