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];
Related
I have an application which is based on animations. After hours of trying and searching the most effective animation processor (which would have low delay and the best memory management) I found out the amazing PNGAnimator (http://www.modejong.com/iPhone/). Funny thing is that I am actually using JPEGs (due to size), but that is not the problem.
The problem is that the class itself is a subclass of UIViewController and when I add it to my rootViewController the new view controller overlaps it - so all the interface elements (buttons...) are hidden under it.
That is why I would like to somehow convert the code to be a subclass of UIView. (So I can add the buttons over it) I tried converting the code myself, however the application would crash on an animation call. Could you please tell me what should I change in the code to so it will act as an UIView?
For example, I tried to change it in .h file to UIView and then in .m file, changing the references self.view to just self. Then in my root view controller adding it as an subview, but that does not show up and crashes either.
Here is the .h file:
//
// ImageAnimatorViewController.h
// PNGAnimatorDemo
//
// Created by Moses DeJong on 2/5/09.
//
#import <UIKit/UIKit.h>
#define ImageAnimator15FPS (1.0/15)
#define ImageAnimator12FPS (1.0/12)
#define ImageAnimator25FPS (1.0/24)
#define ImageAnimator18FPS (1.0/18)
#define ImageAnimatorDidStartNotification #"ImageAnimatorDidStartNotification"
#define ImageAnimatorDidStopNotification #"ImageAnimatorDidStopNotification"
#class AVAudioPlayer;
#interface ImageAnimatorViewController : UIViewController {
#public
NSArray *animationURLs;
NSTimeInterval animationFrameDuration;
NSInteger animationNumFrames;
NSInteger animationRepeatCount;
UIImageOrientation animationOrientation;
NSURL *animationAudioURL;
AVAudioPlayer *avAudioPlayer;
#private
UIImageView *imageView;
NSArray *animationData;
NSTimer *animationTimer;
NSInteger animationStep;
NSTimeInterval animationDuration;
NSTimeInterval lastReportedTime;
}
// public properties
#property (nonatomic, copy) NSArray *animationURLs;
#property (nonatomic, assign) NSTimeInterval animationFrameDuration;
#property (nonatomic, readonly) NSInteger animationNumFrames;
#property (nonatomic, assign) NSInteger animationRepeatCount;
#property (nonatomic, assign) UIImageOrientation animationOrientation;
#property (nonatomic, retain) NSURL *animationAudioURL;
#property (nonatomic, retain) AVAudioPlayer *avAudioPlayer;
#property (nonatomic, assign) CGRect viewCGRect;
// private properties
#property (nonatomic, retain) UIImageView *imageView;
#property (nonatomic, copy) NSArray *animationData;
#property (nonatomic, retain) NSTimer *animationTimer;
#property (nonatomic, assign) NSInteger animationStep;
#property (nonatomic, assign) NSTimeInterval animationDuration;
+ (ImageAnimatorViewController*) imageAnimatorViewController;
- (void) startAnimating;
- (void) stopAnimating;
- (BOOL) isAnimating;
- (void) animationShowFrame: (NSInteger) frame;
+ (NSArray*) arrayWithNumberedNames:(NSString*)filenamePrefix
rangeStart:(NSInteger)rangeStart
rangeEnd:(NSInteger)rangeEnd
suffixFormat:(NSString*)suffixFormat;
+ (NSArray*) arrayWithResourcePrefixedURLs:(NSArray*)inNumberedNames;
#end
And here the .m file:
//
// ImageAnimatorViewController.m
// PNGAnimatorDemo
//
// Created by Moses DeJong on 2/5/09.
//
#import "ImageAnimatorViewController.h"
#import <QuartzCore/QuartzCore.h>
#import <AVFoundation/AVAudioPlayer.h>
#implementation ImageAnimatorViewController
#synthesize animationURLs, animationFrameDuration, animationNumFrames, animationRepeatCount,
imageView, animationData, animationTimer, animationStep, animationDuration, animationOrientation, viewCGRect;
#synthesize animationAudioURL, avAudioPlayer;
- (void)dealloc {
// This object can't be deallocated while animating, this could
// only happen if user code incorrectly dropped the last ref.
NSAssert([self isAnimating] == FALSE, #"dealloc while still animating");
self.animationURLs = nil;
self.imageView = nil;
self.animationData = nil;
self.animationTimer = nil;
[super dealloc];
}
+ (ImageAnimatorViewController*) imageAnimatorViewController
{
return [[[ImageAnimatorViewController alloc] init] autorelease];
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
// Implement loadView to create a view hierarchy programmatically, without using a nib.
- (void)loadView {
UIView *myView = [[UIView alloc] initWithFrame:viewCGRect];
[myView autorelease];
self.view = myView;
/*UIView *myView = [[UIView alloc] initWithFrame:[UIScreen mainScreen].applicationFrame];
[myView autorelease];
self.view = myView;
// FIXME: Additional Supported Orientations
if (animationOrientation == UIImageOrientationUp) {
// No-op
} else if (animationOrientation == UIImageOrientationLeft) {
// 90 deg CCW
//[self rotateToLandscape];
} else if (animationOrientation == UIImageOrientationRight) {
// 90 deg CW
//[self rotateToLandscapeRight];
} else {
NSAssert(FALSE,#"Unsupported animationOrientation");
}
*/
// Foreground animation images
UIImageView *myImageView = [[UIImageView alloc] initWithFrame:self.view.frame];
[myImageView autorelease];
self.imageView = myImageView;
// Animation data should have already been loaded into memory as a result of
// setting the animationURLs property
NSAssert(animationURLs, #"animationURLs was not defined");
NSAssert([animationURLs count] > 1, #"animationURLs must include at least 2 urls");
NSAssert(animationFrameDuration, #"animationFrameDuration was not defined");
// Load animationData by reading from animationURLs
NSMutableDictionary *dataDict = [NSMutableDictionary dictionaryWithCapacity:[animationURLs count]];
NSMutableArray *muArray = [NSMutableArray arrayWithCapacity:[animationURLs count]];
for ( NSURL* aURL in animationURLs ) {
NSString *urlKey = aURL.path;
NSData *dataForKey = [dataDict objectForKey:urlKey];
if (dataForKey == nil) {
dataForKey = [NSData dataWithContentsOfURL:aURL];
NSAssert(dataForKey, #"dataForKey");
[dataDict setObject:dataForKey forKey:urlKey];
}
[muArray addObject:dataForKey];
}
self.animationData = [NSArray arrayWithArray:muArray];
int numFrames = [animationURLs count];
float duration = animationFrameDuration * numFrames;
self->animationNumFrames = numFrames;
self.animationDuration = duration;
[self.view addSubview:imageView];
// Display first frame of image animation
self.animationStep = 0;
[self animationShowFrame: animationStep];
self.animationStep = animationStep + 1;
if (animationAudioURL != nil) {
AVAudioPlayer *avPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:animationAudioURL
error:nil];
[avPlayer autorelease];
NSAssert(avPlayer, #"AVAudioPlayer could not be allocated");
self.avAudioPlayer = avPlayer;
[avAudioPlayer prepareToPlay];
}
}
// Create an array of file/resource names with the given filename prefix,
// the file names will have an integer appended in the range indicated
// by the rangeStart and rangeEnd arguments. The suffixFormat argument
// is a format string like "%02i.png", it must format an integer value
// into a string that is appended to the file/resource string.
//
// For example: [createNumberedNames:#"Image" rangeStart:1 rangeEnd:3 rangeFormat:#"%02i.png"]
//
// returns: {"Image01.png", "Image02.png", "Image03.png"}
+ (NSArray*) arrayWithNumberedNames:(NSString*)filenamePrefix
rangeStart:(NSInteger)rangeStart
rangeEnd:(NSInteger)rangeEnd
suffixFormat:(NSString*)suffixFormat
{
NSMutableArray *numberedNames = [[NSMutableArray alloc] initWithCapacity:40];
for (int i = rangeStart; i <= rangeEnd; i++) {
NSString *suffix = [NSString stringWithFormat:suffixFormat, i];
NSString *filename = [NSString stringWithFormat:#"%#%#", filenamePrefix, suffix];
[numberedNames addObject:filename];
}
NSArray *newArray = [NSArray arrayWithArray:numberedNames];
[numberedNames release];
return newArray;
}
// Given an array of resource names (as returned by arrayWithNumberedNames)
// create a new array that contains these resource names prefixed as
// resource paths and wrapped in a NSURL object.
+ (NSArray*) arrayWithResourcePrefixedURLs:(NSArray*)inNumberedNames
{
NSMutableArray *URLs = [[NSMutableArray alloc] initWithCapacity:[inNumberedNames count]];
NSBundle* appBundle = [NSBundle mainBundle];
for ( NSString* path in inNumberedNames ) {
NSString* resPath = [appBundle pathForResource:path ofType:nil];
NSURL* aURL = [NSURL fileURLWithPath:resPath];
[URLs addObject:aURL];
}
NSArray *newArray = [NSArray arrayWithArray:URLs];
[URLs release];
return newArray;
}
// Invoke this method to start the animation
- (void) startAnimating
{
self.animationTimer = [NSTimer timerWithTimeInterval: animationFrameDuration
target: self
selector: #selector(animationTimerCallback:)
userInfo: NULL
repeats: TRUE];
[[NSRunLoop currentRunLoop] addTimer: animationTimer forMode: NSDefaultRunLoopMode];
animationStep = 0;
if (avAudioPlayer != nil)
[avAudioPlayer play];
// Send notification to object(s) that regestered interest in a start action
[[NSNotificationCenter defaultCenter]
postNotificationName:ImageAnimatorDidStartNotification
object:self];
}
// Invoke this method to stop the animation, note that this method must not
// invoke other methods and it must cancel any pending callbacks since
// it could be invoked in a low-memory situation or when the object
// is being deallocated. Invoking this method will not generate a
// animation stopped notification, that callback is only invoked when
// the animation reaches the end normally.
- (void) stopAnimating
{
if (![self isAnimating])
return;
[animationTimer invalidate];
self.animationTimer = nil;
animationStep = animationNumFrames - 1;
[self animationShowFrame: animationStep];
if (avAudioPlayer != nil) {
[avAudioPlayer stop];
avAudioPlayer.currentTime = 0.0;
self->lastReportedTime = 0.0;
}
// Send notification to object(s) that regestered interest in a stop action
[[NSNotificationCenter defaultCenter]
postNotificationName:ImageAnimatorDidStopNotification
object:self];
}
- (BOOL) isAnimating
{
return (animationTimer != nil);
}
// Invoked at framerate interval to implement the animation
- (void) animationTimerCallback: (NSTimer *)timer {
if (![self isAnimating])
return;
NSTimeInterval currentTime;
NSUInteger frameNow;
if (avAudioPlayer == nil) {
self.animationStep += 1;
// currentTime = animationStep * animationFrameDuration;
frameNow = animationStep;
} else {
currentTime = avAudioPlayer.currentTime;
frameNow = (NSInteger) (currentTime / animationFrameDuration);
}
// Limit the range of frameNow to [0, SIZE-1]
if (frameNow == 0) {
frameNow = 0;
} else if (frameNow >= animationNumFrames) {
frameNow = animationNumFrames - 1;
}
[self animationShowFrame: frameNow];
// animationStep = frameNow + 1;
if (animationStep >= animationNumFrames) {
[self stopAnimating];
// Continue to loop animation until loop counter reaches 0
if (animationRepeatCount > 0) {
self.animationRepeatCount = animationRepeatCount - 1;
[self startAnimating];
}
}
}
// Display the given animation frame, in the range [1 to N]
// where N is the largest frame number.
- (void) animationShowFrame: (NSInteger) frame {
if ((frame >= animationNumFrames) || (frame < 0))
return;
NSData *data = [animationData objectAtIndex:frame];
UIImage *img = [UIImage imageWithData:data];
imageView.image = img;
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
self.animationRepeatCount = 0;
[self stopAnimating];
}
#end
Thank you very much for any advice!
Not sure why you got downvoted. I think your issue is that the UIViewController subclass has the loadView function implemented, and depends on it to build the UI. In the case of a view controller, this function is called automatically to build the view if it is not loaded from a .xib. You can try calling this functional manually after you create your object, and it should do pretty much the same thing.
Keep in mind that the shouldAutorotateToInterfaceOrientation
function is also a UIViewController function and will never be called on a UIView
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
Anyidea why autocomplete does not work on the spaceScene property?
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#class SpaceScene;
#interface SpaceSceneSingleton : NSObject
{
SpaceScene *spaceScene;
}
#property (assign) SpaceScene *spaceScene;
+(SpaceSceneSingleton*)sharedSpaceSceneSingleton;
-(void) addChildToSceneWith:(CCNode *) node andWithZindex: (int) zIndex;
-(void) runAction:(CCAction*) action;
-(void) setTouchIsEnabled:(BOOL) isEnabled;
-(void) removeChild: (CCNode *) child;
#end
#import "SpaceSceneSingleton.h"
#implementation SpaceSceneSingleton
#synthesize spaceScene;
static SpaceSceneSingleton* _sharedSpaceSceneSingleton = nil;
+(SpaceSceneSingleton*)sharedSpaceSceneSingleton;
{
#synchronized([SpaceSceneSingleton class])
{
if (!_sharedSpaceSceneSingleton)
[[self alloc] init];
return _sharedSpaceSceneSingleton;
}
return nil;
}
+(id)alloc
{
#synchronized([SpaceSceneSingleton class])
{
NSAssert(_sharedSpaceSceneSingleton == nil, #"Attempted to allocate a second instance of a singleton.");
_sharedSpaceSceneSingleton = [super alloc];
return _sharedSpaceSceneSingleton;
}
return nil;
}
-(id)init {
self = [super init];
if (self != nil) {
// initialize stuff here
}
return self;
}
-(void) addChildToSceneWith:(CCNode *) node andWithZindex: (int) zIndex
{
[self.spaceScene addChild:node z:zIndex];
}
-(void) runAction:(CCAction*) action
{
//[self.spaceScene add
}
-(void) setTouchIsEnabled:(BOOL) isEnabled
{
}
-(void) removeChild: (CCNode *) child
{
}
#end
You only declared #class SpaceScene; so within this scope nothing more is known than that a class called SpaceScene might exist. Maybe importing SpaceScene.h helps.
I would even say this should compile with warnings. Does it?
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;
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?