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:
If you want to declare the property readonly, you can once again declare the property inside your .m to make it writeable internally:
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:
This here is unnecessary
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
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