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
Related
I want to have an internal int array for my class, but I can't seem to get XCode to let me. The array size needs to be set on initialization so I can't put the size directly into the interface.
At the moment I've been trying:
#interface TestClass : NSObject {
int test[];
}
But it tells me that I'm not allowed. How to I refer to it in my interface, and then how do I allocate it when I create the implementation?
Sorry for a somewhat standard sounding question, but I can't seem to find the answer I need from searching.
edit: I want to use an array because it's apparently much faster than using an NSArray
You can use a number of methods to overcome this problem, but the easiest is to simply make the instance variable a pointer, like this:
#interface TestClass : NSObject {
int *test;
}
#property int *test;
#end
Synthesizing the property will give it getter and setter methods which you can use to set its contents:
#implementation TestClass
#synthesize test;
//contents of class
#end
You can then use it like this:
TestClass *pointerTest = [[TestClass alloc] init];
int *array = (int *)malloc(sizeof(int) * count);
//set values
[pointerTest setTest:array];
[pointerTest doSomething];
However, using objects like NSNumber in an NSArray is a better way to go, perhaps you could do something like this:
#interface TestClass : NSObject {
NSArray *objectArray;
}
#property (nonatomic, strong) NSArray *objectArray;
#end
#implementation TestClass
#synthesize objectArray;
//contents of class
#end
You can then set its contents with a pointer to an NSArray object:
NSArray *items = [NSArray arrayWithObjects:[NSNumber numberWithInt:1], [NSNumber numberWithInt:2], nil];
TestClass *arrayClass = [[TestClass alloc] init];
[arrayClass setItems:items];
[arrayClass doSomething];
When retaining objects upon setting them (like the previous example), always make sure you deallocate the object in the classes dealloc method.
A C array is just a sufficiently sized raw memory buffer. Foundation has a nice wrapper around raw memory that frees you from all the manual memory management: NSMutableData
The following approach gives you automatic memory management plus proper encapsulation.
#interface TestClass : NSObject
#property (nonatomic, readonly) int *testArray;
#property (nonatomic, readonly) NSUInteger testArraySize;
#end
#implementation TestClass
{
NSMutableData *_testData;
}
- (id)initWithSize:(NSUInteger)size
{
self = [self init];
if (self != nil) {
_testData = [NSMutableData dataWithLength:size];
}
}
- (int *)testArray
{
return [_testData mutableBytes];
}
- (NSUInteger)testArraySize
{
return [_testData length];
}
#end
As you see, the ivar does not have to be declared in the #interface.
Try something like this:
#interface TestClass : NSObject
{
int *_test;
}
#property (assign) int *test;
#end
#implementation TestClass
- (instancetype)init
{
if (self = [super init])
{
_test = malloc(sizeof(int) * 20);
}
return self;
}
- (int *)test
{
return _test;
}
- (void)setTest:(int*)test
{
memcpy(&_test, &test, sizeof(_test));
}
- (void)dealloc
{
free(_test);
}
#end
In my card matching game:
-I have a method that checking cards that were flipped in certain index. It's basically the whole logic in the app.
-I have another method that checks the matching.
Now, I created a switch button in my view controller that will tell the controller that the user changed the mode for "3" cards instead of the basic mode (2 cards).
My issue is, how do i tell the controller to check in the matching method if there is more than 2 matches..it's driving me crazy please try to help me figure this out.
I also have in the controller an updateUI method that making cards that are matched fade away so I need to make sure it behaves the same.
The following code show's the flipCardAtIndex method, matching method & view controller in the same order:
CardMatchingGame.m (the last method is flipCardAtIndex):
#import "CardMatchingGame.h"
#import "PlayingCardsDeck.h"
#interface CardMatchingGame()
#property (readwrite, nonatomic) int score;
#property (strong, nonatomic) NSMutableArray *cards;
#property (strong, nonatomic) NSString *notification;
#end
#implementation CardMatchingGame
-(NSMutableArray *) cards {
if (!_cards) _cards = [[NSMutableArray alloc] init];
return _cards;
}
-(id)initWithCardCount:(NSUInteger)count usingDeck:(Deck *)deck {
self = [super init];
if (self) {
for (int i = 0; i < count; i++) {
Card *card = [deck drawRandonCard];
if (!card) {
self = nil;
} else {
self.cards[i] = card;
}
}
}
return self;
}
-(Card *) cardAtIndex:(NSUInteger)index {
return (index < self.cards.count) ? self.cards[index] : nil;
}
#define FLIP_COST 1
#define MISMATCH_PENALTY 2
#define BONUS 4
-(void) flipCardAtIndex:(NSUInteger)index {
Card *card = [self cardAtIndex:index];
if (!card.isUnplayable) {
if (!card.isFaceUp) {
for (Card *otherCard in self.cards) {
if (otherCard.isFaceUp && !otherCard.isUnplayable) {
NSMutableArray *myCards = [[NSMutableArray alloc] init];
[myCards addObject:otherCard];
int matchScore = [card match:myCards];
if (matchScore) {
otherCard.unplayble = YES;
card.unplayble = YES;
self.notification = [NSString stringWithFormat:#"%# & %# match!", card.contents, otherCard.contents];
self.score += matchScore * BONUS;
} else {
otherCard.faceUp = NO;
self.score -= MISMATCH_PENALTY;
self.notification = [NSString stringWithFormat:#"%# did not matched to %#", card.contents, otherCard.contents];
}
break;
}
}
self.score -= FLIP_COST;
}
card.faceUp = !card.isFaceUp;
}
}
#end
PlayingCards.m (Only the first method, matching method) :
#import "PlayingCards.h"
#implementation PlayingCards
#synthesize suit = _suit;
//overriding the :match method of cards to give different acore if its only a suit match or a number match
-(int)match:(NSArray *)cardToMatch {
int score = 0;
for (int i = 0; i < cardToMatch.count; i++) {
PlayingCards *nextCard = cardToMatch[i];
if ([nextCard.suit isEqualToString:self.suit]) {
score += 1;
} else if (nextCard.rank == self.rank) {
score += 4;
}
}
return score;
}
My view controller (the last method is the one for the switch button) :
#import "CardGameViewController.h"
#import "PlayingCardsDeck.h"
#import "CardMatchingGame.h"
#interface CardGameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (weak, nonatomic) IBOutlet UILabel *notificationLabel;
#property (weak, nonatomic) IBOutlet UILabel *scoreCounter;
#property (strong, nonatomic) IBOutletCollection(UIButton) NSArray *cardButtons;
#property (strong, nonatomic) CardMatchingGame *game;
#property (nonatomic) int flipsCount;
#property (nonatomic) NSNumber *mode;
//#property (weak, nonatomic) IBOutlet UISwitch *mySwitch;
#property (weak, nonatomic) IBOutlet UISwitch *mySwitch;
#end
#implementation CardGameViewController
#synthesize mode = _mode;
//creating the getter method that creates a new card game.
-(CardMatchingGame *) game {
if (!_game) _game = [[CardMatchingGame alloc] initWithCardCount:self.cardButtons.count usingDeck:[[PlayingCardsDeck alloc] init]];
return _game;
}
//creating a setter for the IBOutletCollection cardButtons
-(void) setCardButtons:(NSArray *)cardButtons {
_cardButtons = cardButtons;
[self updateUI];
}
//creating the setter for the flipCount property. Whick is setting the flipsLabel to the right text and adding the number of counts.
-(void) setFlipsCount:(int)flipsCount {
_flipsCount = flipsCount;
self.flipsLabel.text = [NSString stringWithFormat:#"Flips: %d", self.flipsCount];
}
-(void) updateUI {
for (UIButton *cardButton in self.cardButtons) {
Card *card = [self.game cardAtIndex:[self.cardButtons indexOfObject:cardButton]];
[cardButton setTitle:card.contents forState:UIControlStateSelected];
[cardButton setTitle:card.contents forState:UIControlStateSelected|UIControlStateDisabled];
cardButton.selected = card.isFaceUp;
cardButton.enabled = !card.unplayble;
if (card.unplayble) {
cardButton.alpha = 0.1;
}
//updating the score
self.scoreCounter.text = [NSString stringWithFormat:#"Score: %d", self.game.score];
//if notification in CardMatchingGame.m is no nil, it will be presented
if (self.game.notification) {
self.notificationLabel.text = self.game.notification;
}
}
}
//Here I created a method to flipCards when the card is selected, and give the user a random card from the deck each time he flips the card. After each flip i'm incrementing the flipCount setter by one.
- (IBAction)flipCard:(UIButton *)sender {
[self.game flipCardAtIndex:[self.cardButtons indexOfObject:sender] forMode:self.mode];
self.flipsCount++;
[self updateUI];
}
//sending an alert if the user clicked on new game button
- (IBAction)newGame:(UIButton *)sender {
UIAlertView* mes=[[UIAlertView alloc] initWithTitle:#"Are you sure..?" message:#"This will start a new game" delegate:self cancelButtonTitle:#"No" otherButtonTitles:#"Yes", nil];
[mes show];
}
//preforming an action according to the user choice for the alert yes/no to start a new game
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
if (buttonIndex != [alertView cancelButtonIndex]) {
self.flipsCount = 0;
self.game = nil;
for (UIButton *button in self.cardButtons) {
Card *card = [self.game cardAtIndex:[self.cardButtons indexOfObject:button]];
card.unplayble = NO;
card.faceUp = NO;
button.alpha = 1;
}
self.notificationLabel.text = nil;
[self updateUI];
}
}
-(void) setMode:(NSNumber *)mode {
mode = _mode;
}
-(void) switchValueChange:(id)sender {
UISwitch *Switch = (UISwitch *) sender;
NSNumber *twoCards = [NSNumber numberWithInt:2];
NSNumber *threeCards = [NSNumber numberWithInt:3];
if (Switch.on) {
self.mode = twoCards;
}
else
{
self.mode = threeCards;
}
}
- (void)viewDidLoad
{
UISwitch *mySwitch;
[super viewDidLoad];
[mySwitch addTarget:self action:#selector(switchValueChange:) forControlEvents:UIControlEventValueChanged];
[self updateUI];
}
#end
Actually, getting the switch value is the easy part. You can get set a referencing outlet from your switch to your viewController (CardGameViewController), and in your view controller's viewDidLoad method, add the method to listen for changes to switch's value:
[mySwitch addTarget:self action:#selector(switchValueChange:) forControlEvents:UIControlEventValueChanged];
Add a new property called NSNumber *mode in your CardGameViewController and synthesize it.
Now you can update the "mode" (which I believe could be an instance variable) in switchValueChanged method:
- (void)switchValueChange:(id)sender
{
UISwitch *switch = (UISwitch *) sender;
if (sender.on)
self.mode = 2;
else
self.mode = 3;
}
If my assumption is right, then by "mode" you mean how many cards to match, is that correct? 2 means, that atleat 2 cards should be same when faced up, and 3 means, that 3 cards should match in suit or number.
Start by changing the match method in your PlayingCards to something like this (accepting another parameter named mode) (You might have to update the same method in its parent class too):
//overriding the :match method of cards to give different acore if its only a suit match or a number match
-(int)match:(NSArray *)cardToMatch forMode:(NSNumber *) mode{
int score = 0;
int cardsMatched = 0;
for (int i = 0; i < cardToMatch.count; i++) {
PlayingCards *nextCard = cardToMatch[i];
if ([nextCard.suit isEqualToString:self.suit]) {
score += 1;
cardsMatched++;
} else if (nextCard.rank == self.rank) {
score += 4;
cardsMatched++;
}
if (cardsMatched >= [mode intValue])
break;
}
return score;
}
Now, in your CardMatchingGame.m method, change the flipCardAtIndex method to this (accepting another parameter named mode):
-(void) flipCardAtIndex:(NSUInteger)index forMode (NSNumber *mode) {
Card *card = [self cardAtIndex:index];
if (!card.isUnplayable) {
if (!card.isFaceUp) {
NSMutableArray *myFaceUpCards = [[NSMutableArray alloc] init];
// UPDATED: Loop through all the cards that are faced up and add them to an array first
for (Card *otherCard in self.cards) {
if (otherCard.isFaceUp && !otherCard.isUnplayable && orderCard != card) {
[myCards addObject:otherCard];
}
// UPDATED: Now call the method to do the match. The match method now takes an extra parameter
int matchScore = [card match:myCards forMode:mode];
if (matchScore) {
otherCard.unplayble = YES;
card.unplayble = YES;
self.notification = [NSString stringWithFormat:#"%# & %# match!", card.contents, otherCard.contents];
self.score += matchScore * BONUS;
} else {
otherCard.faceUp = NO;
self.score -= MISMATCH_PENALTY;
self.notification = [NSString stringWithFormat:#"%# did not matched to %#", card.contents, otherCard.contents];
}
}
self.score -= FLIP_COST;
}
card.faceUp = !card.isFaceUp;
}
}
Finally, change call to
[self.game flipCardAtIndex:[self.cardButtons indexOfObject:sender]];
in
- (IBAction)flipCard:(UIButton *)sender
method of CardGameViewController to
[self.game flipCardAtIndex:[self.cardButtons indexOfObject:sender] forMode:self.mode];
Take a look and see if that makes sense.
Remove #property (weak, nonatomic) IBOutlet UISwitch *mySwitch; from your .m class.
Instead, go to your storyboard, click on the controller that has the switch, then click on the Assistant Editor button on top right (looks like a face). It will open up CardGameViewController.h. Now right click on the switch view on the storyoard, and click and drag from New Referencing Outlet to CardViewController.h. This is how you reference your switch into your controller.
Now that you have the switch variable in the interface file, go to your implementation file (CardGameViewController.m) and synthesize the variable:
#synthesize mySwitch = _mySwitch;
Now, change your viewDidLoad method to this:
- (void)viewDidLoad
{
[super viewDidLoad];
[self.mySwitch addTarget:self action:#selector(switchValueChange:) forControlEvents:UIControlEventValueChanged];
[self updateUI];
}
Also remove setMode method. If you are synthesizing mode variable, then you don't need it.
Give it a try now. You know how to debug in xcode using breakpoints?
My GameScene.m file :
#import "GameScene.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#pragma mark - GameScene
// GameScene implementation
#implementation GameScene
// on "init" you need to initialize your instance
-(id) init
{
self = [super init];
if (self != nil)
{
[self addChild:[GameLayer node]];
}
return self;
}
- (void) dealloc
{
[super dealloc];
}
#end
#implementation GameLayer
#synthesize hero;
-(id) init
{
if( (self=[super init] ))
{
hero = [[Hero alloc] initWithGame:self];
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
#end
My GameScene.h file :
#import <GameKit/GameKit.h>
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
// GameScene
#interface GameScene : CCScene
{
}
#end
#class Hero;
#interface GameLayer : CCLayer
{
Hero * _hero;
NSMutableArray * _bullets;
bool _playerFiring;
NSMutableArray * _enemies;
float _lastTimeEnemyLaunched;
float _enemyInterval;
int _score;
int _lives;
int _bombs;
int _level;
}
#property (nonatomic,retain) Hero * hero;
#property (nonatomic,readwrite) bool playerFiring;
#property (nonatomic,readwrite) float lastTimeEnemyLaunched;
#property (nonatomic,readwrite) float enemyInterval;
#property (nonatomic,retain) NSMutableArray * enemies;
#property (nonatomic,retain) NSMutableArray * bullets;
#property (assign,readwrite) int score;
#property (assign,readwrite) int lives;
#property (assign,readwrite) int bombs;
#property (assign,readwrite) int level;
#end
My Hero.h file :
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "GameScene.h"
#class GameLayer;
#interface Hero : CCNode
{
CCSprite * mySprite;
GameLayer * theGame;
float _lastTimeFired;
float _fireInterval;
float _firingSpeed;
float _movementSpeed;
}
#property (nonatomic,retain) CCSprite * mySprite;
#property (nonatomic,retain) GameLayer * theGame;
#property (nonatomic,readwrite) float lastTimeFired;
#property (nonatomic,readwrite) float fireInterval;
#property (nonatomic,readwrite) float firingSpeed;
#property (nonatomic,readwrite) float movementSpeed;
#end
And my Hero.m file :
#import "Hero.h"
#implementation Hero
#synthesize theGame,mySprite;
-(id) initWithGame:(GameLayer *)game
{
self = [super init];
if(self != nil)
{
// ask director for the window size
CGSize size = [[CCDirector sharedDirector] winSize];
self.theGame = game;
mySprite = [CCSprite spriteWithFile:#"hero.png"];
[theGame addChild:mySprite z:2];
[mySprite setPosition:ccp(size.width/2,50)];
self.lastTimeFired = 0;
self.fireInterval = 3;
self.firingSpeed = 10;
self.movementSpeed = 5;
}
return self;
}
-(void) dealloc
{
[super dealloc];
}
#end
And here's my problem : I get two warnings - 1. "instance method -initWithGame not found (return type default to 'id')" and 2. "Receiver 'Hero' is a forward class and corresponding #interface may not exist"
I tried to add "-(id) initWithGame:(GameLayer *)game" line to Hero.h interface, but it won't work. I tried to add that line but with + instead of -, but nothing.
I end up without my Hero displayed on a screen. Does anyone knows how to solve this problem (I use newest version of Xcode)?
In GameScene.m, you should
#import "Hero.h"
This explains why you get the "forward class" warning: since you did not import the header, the only thing known to the compiler in the GameScene compilation unit is the forward declaration.
Once you do that, if you also declare initWithGame in Hero.h, then you won't get any warning.
New to ios programming so for practice I am trying to make a simple app with a playing card with it's back on the view which you can tap to reveal the front of the card. The front of the card is different every time you tap it.
What I am trying to achieve is that when i tap the card:
it flips and displays a random card,
have a label that increments by 1 every time the card is flipped
I am done with the second requirement but I can't seem to get the first one right. I have 3 models(Card, Deck, PlayingCard)
My Deck model is responsible for randomising the cards and here are the codes.
Deck.h
#import <Foundation/Foundation.h>
#import "Card.h"
#interface Deck : NSObject
- (void)addCard:(Card *)card atTop:(BOOL)atTop;
- (Card *)drawRandomCard;
#end
Deck.m
#import "Deck.h"
#interface Deck()
#property (strong, nonatomic) NSMutableArray *cards;
#end
#implementation Deck
-(NSMutableArray *)cards
{
if(!_cards) _cards =[[NSMutableArray alloc] init];
return _cards;
}
- (void)addCard:(Card *)card atTop:(BOOL)atTop
{
if(card){
if(atTop){
[self.cards insertObject:card atIndex:0];
}
else
{
[self.cards addObject:card];
}
}
}
- (Card *)drawRandomCard
{
Card *randomCard = nil;
if(self.cards.count){
unsigned index = arc4random() % self.cards.count;
randomCard = self.cards[index];
[self.cards removeObjectAtIndex:index];
}
return randomCard;
}
#end
My PlayingCard basically describes the contents of a Card. for eg. Jack of Hearts, 2 of Diamonds where Jack would be the "rank" of the card and "hearts" would be the suit of the card which together form the contents of the card.
PlayingCard.h
#import <Foundation/Foundation.h>
#import "Card.h"
#interface PlayingCard : NSObject
#property (strong, nonatomic) NSString *suit;
#property (nonatomic) NSUInteger rank;
+ (NSArray *) validSuits;
+ (NSUInteger) maxRank;
- (NSString *)contents;
#end
PlayingCard.m
#import "PlayingCard.h"
#implementation PlayingCard
- (NSString *)contents
{
NSArray *rankStrings = [PlayingCard rankStrings];
return [rankStrings[self.rank] stringByAppendingString:self.suit];
}
#synthesize suit = _suit;
+(NSArray *)validSuits{
return #[#"♥", #"♦", #"♠", #"♣"] ;
}
+(NSArray *)rankStrings{
return #[# "?", #"A", #"2", #"3", #"4", #"5", #"6", #"7", #"8", #"9",#"10", #"J", #"Q", #"K"];
}
-(void)setSuit:(NSString *)suit
{
if([[PlayingCard validSuits] containsObject:suit]){
_suit = suit;
}
}
-(NSString *)suit{
return _suit ? _suit: #"?";
}
+ (NSUInteger)maxRank {
return [self rankStrings].count-1;
}
-(void)setRank:(NSUInteger)rank{
if(rank <= [PlayingCard maxRank]) {
_rank = rank;
}
}
#end
Finally my ViewController.m
#import "CardGameViewController.h"
#import "Card.h"
#import "Deck.h"
#import "PlayingCard.h"
#interface CardGameViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (nonatomic) NSString *title;
#property (weak, nonatomic) IBOutlet UIButton *cardRandom;
#end
#implementation CardGameViewController
- (void)setFlipCount:(int)flipCount
{
_flipCount = flipCount;
self.flipsLabel.text = [NSString stringWithFormat:#" Flips:%d", self.flipCount ];
}
- (IBAction)flipCard:(UIButton *)sender
{
if(sender.isSelected) {
sender.selected = NO;
self.flipCount ++;
}
else{
sender.selected = YES;
self.flipCount ++;
}
}
#end
I researched about this and i found that I should probably use
- (void)setTitle:(NSString *)title forState:(UIControlState)state
where the state will be UIControlStateSelected because the front of the card containing the contents is the selected content of the button but I dont know how to set the title according to the random card content developed by my model.
Lets assume you have a property of the class Deck (called cardDeck) in your CardGameViewController, and you have already populated that property with cards using addCard method.
Now when the user taps flip button, you should do something like this ::
- (IBAction)flipCard:(UIButton *)sender
{
//draw a random card
Card *randomCard = [self.cardDeck drawRandomCard];
//set the button title
[sender setTitle:[randomCard contents] forState:UIControlStateSelected];
if(sender.isSelected) {
sender.selected = NO;
self.flipCount ++;
}
else{
sender.selected = YES;
self.flipCount ++;
}
}
Hope this helps
PS :: drawRandomCard method has a return type "Card", it seems it should be "PlayingCard", or the PlayingCard class should be named Card.
You aren't initializing your playing card deck. Your ViewController.m file should look like this (I also reset the deck once all cards have been shown in the flipCard method):
#import "CardViewViewController.h"
#import "PlayingCardDeck.h"
#interface CardViewViewController ()
#property (weak, nonatomic) IBOutlet UILabel *flipsLabel;
#property (nonatomic) int flipCount;
#property (nonatomic) PlayingCardDeck *myDeck;
#end
#implementation CardViewViewController
-(PlayingCardDeck *)myDeck
{
if(!_myDeck) _myDeck=[[PlayingCardDeck alloc] init];
return _myDeck;
}
-(void)setFlipCount:(int)flipCount
{
_flipCount= (int) flipCount;
self.flipsLabel.text=[NSString stringWithFormat:#"Flips: %d",self.flipCount];
}
- (IBAction)flipCard:(UIButton *)sender
{
if (!sender.isSelected)
{
if (self.flipCount >= 52)
{
self.flipCount = 0;
self.myDeck = nil;
}
Card *cCard = [self.myDeck drawRandomCard];
[sender setTitle: [cCard contents] forState:UIControlStateSelected];
self.flipCount++;
}
sender.selected = !sender.isSelected;
}
#end
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?