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?
Related
I am learning Cocos2D from the Ray Wenderlich book in my spare time but also making changes to it if it does not explain how to do something that I could see myself needing in the future. I am currently stuck because of my very limited knowledge.
I wish to use the GamePlayLayer Class to randomly change the character state of the TBT class.
I have checked each State and they all work (checked by making them the spawn state) so the problem is most likely in the gamePlayLayer.m file where I have attempted to make my own method.
I seem to be able to get it to change its state to kStateThrowing but it changes straight back (in 0.3 of a second!) to kStateIdle before the animation plays and has the following output...
2013-11-21 13:45:07.938 Spaceviking[981:12c03] CHANGING STATE TBT!!!!!!
2013-11-21 13:45:07.939 Spaceviking[981:12c03] TBT->Changing State to throwing
2013-11-21 13:45:07.967 Spaceviking[981:12c03] TBT Going to Idle
2013-11-21 13:45:07.970 Spaceviking[981:12c03] TBT->changing state to idle
Any help with this would be greatly appreciated.
Code listed below...
// CommonProtocols.h
#ifndef SpaceViking_CommonProtocols_h
#define SpaceViking_CommonProtocols_h
typedef enum {
kDirectionLeft,
kDirectionRight
}
PhaserDirection;
typedef enum {
kStatespawning,
kStateIdle,
kStateWalking,
kStateIdleTilt,
kStatebackTilting,
kStateforwardTilting,
kStateAttacking,
kStateTakingDamage,
kStateThrowing,
kStateLosingALife,
kStateDead,
kStateTravelling,
kStateRotating,
kStatetest
}
CharacterStates; //1
typedef enum {
kObjectTypeNone,
kPowerUpTypeHealth,
kPowerTypeMallet,
kEnemyType1BT,
kEnemyType2BT,
kEnemyType3BT,
kEnemyTypePhaser,
kVikingType,
} GameObjectType;
#protocol GamePlayLayerDelegate
-(void)createObjectOfType:(GameObjectType)objectType
withHealth:(int)initialHealth
atLocation:(CGPoint)spawnLocation
withZValue:(int)ZValue;
-(void)createPhaserWithDirection: (PhaserDirection)phaserDirection
andPosition:(CGPoint)spawnPosition;
#endif
// GamePlayLayer.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "CCLayer.h"
#import "SneakyJoystick.h"
#import "SneakyButton.h"
#import "SneakyButtonSkinnedBase.h"
#import "SneakyJoystickSkinnedBase.h"
#import "Constants.h"
#import "CommonProtocols.h"
#import "TBT.h"
#interface GamePlayLayer : CCLayer <GamePlayLayerDelegate> {
CCSprite *vikingSprite;
SneakyJoystick *leftJoystick;
SneakyButton *jumpButton;
SneakyButton *attackButton;
CCSpriteBatchNode *sceneSpriteBatchNode;
}
#end
------------------------------------------------
// GamePlayLayer.m
#import "GamePlayLayer.h"
#implementation GamePlayLayer
-(void) dealloc {
[leftJoystick release];
[jumpButton release];
[attackButton release];
[super dealloc];
}
--DELETED BELOW 2 METHODS AS NOT NEEDED FOR THIS QUESTION--
-(void)initJoystickAndButtons {
}
-(void)applyJoystick:(SneakyJoystick *)aJoystick toNode: (CCNode *)tempNode forTimeDelta:(float)deltaTime
{
}
-(void) update:(ccTime)deltaTime {
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
}
-(void) createObjectOfType: (GameObjectType)objectType
withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
if (objectType == kEnemyType1BT) {
CCLOG(#"creating 1BT");
TBT *tBT = [[TBT alloc] initWithSpriteFrameName:#"BT_anim_1.png"];
[tBT setCharacterHealth:initialHealth];
[tBT setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:tBT
z:ZValue
tag:k1BTTagValue];
[tBT release];
}
}
--BELOW IS THE METHOD I HAVE CREATED TO CHANGE THE STATE--
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
int x = (arc4random() % 3);
TBT *tBT = [[TBT alloc]
initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"BT_anim_1.png"]];
if (x>0) {
CCLOG(#"CHANGING STATE TBT!!!!!!");
[tBT changeState:kStateThrowing];
[tBT setDelegate:self];
[tBT release];
}
}
-(id)init {
self = [super init];
if (self !=nil) {
CGSize screenSize = [CCDirector sharedDirector]. winSize;
self.TouchEnabled = YES;
srandom(time(NULL));
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlas.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"]; // 2
} else {
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlasiPhone.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlasiPhone.png"];
}
[self addChild:sceneSpriteBatchNode z:0];
[self initJoystickAndButtons];
BC *viking = [[BC alloc]
initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"BCmoving_anim_1.png"]];
[viking setJumpButton:jumpButton];
[viking setAttackButton:attackButton];
[viking setPosition:ccp(screenSize.width * 0.19f,
screenSize.height * 0.19f)];
[viking setCharacterHealth:3];
[sceneSpriteBatchNode
addChild:viking
z:kVikingSpriteZValue
tag:kVikingSpriteTagValue];
[self createObjectOfType:kEnemyType1BT withHealth:3 atLocation:ccp(screenSize.width * 0.0439f, screenSize.height * 0.822f) withZValue:10];
---THE BELOW SELECTOR IS WHAT I HAVE USED TO DO A TIMED STATE CHANGE (METHOD ABOVE)--
[self schedule:#selector(changeState:) interval:1.0f];
[self scheduleUpdate];
}
return self;
}
#end
// TBT.h
#import <Foundation/Foundation.h>
#import "GameCharacter.h"
#interface TBT : GameCharacter {
//This is for the required animations
CCAnimation *tiltingAnim;
CCAnimation *transmittingAnim;
CCAnimation *loseLifeAnim;
CCAnimation *throwingAnim;
CCAnimation *afterThrowingAnim;
CCAnimation *shootPhaserAnim;
GameCharacter *vikingCharacter;
id <GamePlayLayerDelegate> delegate;
}
#property (nonatomic,assign) id <GamePlayLayerDelegate> delegate;
#property (nonatomic, retain) CCAnimation *tiltingAnim;
#property (nonatomic, retain) CCAnimation *transmittingAnim;
#property (nonatomic, retain) CCAnimation *loseLifeAnim;
#property (nonatomic, retain) CCAnimation *throwingAnim;
#property (nonatomic,retain) CCAnimation *afterThrowingAnim;
#property (nonatomic,retain) CCAnimation *shootPhaserAnim;
-(void)initAnimations;
-(void)changeState:(CharacterStates)newState;
#end
// TBT.m
#import "TBT.h"
#implementation TBT
#synthesize delegate;
#synthesize tiltingAnim;
#synthesize transmittingAnim;
#synthesize loseLifeAnim;
#synthesize throwingAnim;
#synthesize afterThrowingAnim;
#synthesize shootPhaserAnim;
-(void) dealloc {
delegate = nil;
[tiltingAnim release];
[transmittingAnim release];
[loseLifeAnim release];
[throwingAnim release];
[afterThrowingAnim release];
[shootPhaserAnim release];
[super dealloc];
}
--BELOW METHOD NOT NEEDED FOR THIS QUESTION
-(void)shootPhaser {}
-(CGRect)TBTboundingBox {
CGRect tbtBoundingBox = [self boundingBox];
float xOffset;
float xCropAmount = tbtBoundingBox.size.width * 0.5482f;
float yCropAmount = tbtBoundingBox.size.height * 0.1f;
tbtBoundingBox =
CGRectMake(tbtBoundingBox.origin.x + xOffset,
tbtBoundingBox.origin.y,
tbtBoundingBox.size.width - xCropAmount,
tbtBoundingBox.size.height - yCropAmount);
return tbtBoundingBox;
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
[self setCharacterState:newState];
switch (newState) {
case kStatespawning:
CCLOG(#"TBT->Changing State to Spwaning");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"BT_anim_1.png"]];
break;
case kStateIdle:
CCLOG(#"TBT->schaning state to idle");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"BT_anim_1.png"]];
break;
case kStateThrowing:
CCLOG(#"TBT->Changing State to throwing");
action = [CCSequence actions:
[CCAnimate actionWithAnimation:throwingAnim
restoreOriginalFrame:NO],
[CCDelayTime actionWithDuration:1.0f],
//[CCAnimate actionWithAnimation:shootPhaserAnim
// restoreOriginalFrame:NO],
[CCCallFunc actionWithTarget:self
selector:#selector(shootPhaser)],
[CCAnimate actionWithAnimation:afterThrowingAnim
restoreOriginalFrame:NO],
[CCDelayTime actionWithDuration:0.1f],
nil];
break;
case kStateLosingALife:
CCLOG(#"TBT->Losing a life");
break;
case kStateDead:
CCLOG(#"TBT->changing state to dead");
action = [CCAnimate actionWithAnimation:loseLifeAnim];
break;
default:
CCLOG(#"unhandled state %d in TBT", newState);
break;
}
if (action !=nil) {
[self runAction:action];
}
}
-(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
if (characterState == kStateDead)
return;
vikingCharacter =
(GameCharacter*)[[self parent]
getChildByTag:kVikingSpriteTagValue];
CGRect vikingBoudingBox =
[vikingCharacter adjustedBoundingBox];
CharacterStates vikingState = [vikingCharacter characterState];
if ((vikingState == kStateAttacking) && (CGRectIntersectsRect ([self adjustedBoundingBox], vikingBoudingBox))) {
if (characterState != kStateTakingDamage) {
[self changeState:kStateTakingDamage];
return;
}
}
if ((([self numberOfRunningActions] == 0) && (characterState != kStateDead)) ) {
CCLOG(#"TBT Going to Idle");
[self changeState:kStateIdle];
return;
}
}
-(void)initAnimations {
[self setTransmittingAnim:[self loadPlistForAnimationWithName:#"transmittingAnim" andClassName:NSStringFromClass([self class])]];
[self setThrowingAnim:[self loadPlistForAnimationWithName:#"throwingAnim" andClassName:NSStringFromClass([self class])]];
[self setAfterThrowingAnim:[self loadPlistForAnimationWithName:#"afterThrowingAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### TBT initialized");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kEnemyType1BT;
[self changeState:kStatespawning];
}
}
return self;
}
#end
My guess is you are not adding the TBT object as a child to anything (i.e. CCScene/CCNode/CCSprite). Try adding it to "sceneSpriteBatchNode" like in the createObjectOfType: method.
[sceneSpriteBatchNode addChild:tBT
z:ZValue
tag:k1BTTagValue];
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.
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?
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
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;