Cocos2d: Preloading animation causes a crash - cocos2d-iphone

I am trying to preload an animation in the init method of my layer. I then call the animation if the screen is touched. The app crashes with no error message as soon as I touch the screen and seems it is to do with calling the preloaded animation. I would like to do it this way as it seems expensive to create the animation every time the screen is touched - which does seems to work though. Any tips greatly appreciated.
Sample Code:
In my header:
#interface Test : CCLayer {
NSMutableArray *wake;
CCSprite* ani;
CCAnimate *animate;
}
#end
In my implementation:
-(id) init {
if( (self=[super init])) {
// enable touches
self.isTouchEnabled = YES;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"ani.plist" texture:[[CCTexture2D alloc] initWithImage:[UIImage imageNamed:#"ani.png"]]];
ani = [CCSprite spriteWithSpriteFrameName:#"ani1.png"]; //comes from .plist file
ani.anchorPoint=ccp(0,0);
ani.position = ccp(700,65);
[self addChild:ani z:30];
wake = [NSMutableArray array];
for(int i = 1; i <= 4; i++) {
[wake addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"ani%d.png",i]]];
}
animate = [CCAnimate actionWithAnimation:[CCAnimation animationWithFrames:wake delay:1.0f] restoreOriginalFrame:FALSE];
}
return self;
}
Handling the touch:
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// run the animation
[ani runAction:animate];
}

Animations in Cocos2d are not designed to be reused. You need to create a new one every time.

Problem solved by creating properties for the array and animation on the class using nonatomic,retain.

You only need to retain the animation but the array can be local.
self.myAnimation = [[CCAnimation animationWithFrames:myAniFramesArray delay:0.1f] retain];
Remember to make the property nonatomic, retain as stated by Chev and to release any objects you retain in the appropriate dealloc method.

Related

Cocos2d - Actions and Animations are not paused

When I do this:
[gameLayer pauseSchedulerAndActions];
Most of the game pauses, but the sprites that are undergoing this action do not pause spinning:
[sprite runAction:[CCRepeatForever actionWithAction:[CCRotateBy actionWithDuration:5.0 angle: 360]]];
Also, those sprites that are running CCAnimations do not stop animating:
CCAnimation *theAnim = [CCAnimation animationWithSpriteFrames:theFrames delay:0.1];
CCSprite *theOverlay = [CCSprite spriteWithSpriteFrameName:#"whatever.png"];
self.theAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:theAnim]];
How can I get these to pause when the game is paused? I would expect “pauseSchedulerAndActions” to pause actions, but that doesn’t seem to be the case.
pauseSchedulerAndActions is not recursive, so it will only affect actions and schedules on the node you are pausing, not it's children.
if your scene/layer is shallow, you could probably just get away with looping through the layer's children and calling (pause/resume)SchedulerAndActions on each, otherwise, if you have a deeper graph, you'll probably want to call it recursively. I wrote up a small test and this worked for me:
-(void) pauseSchedulerAndActions: (BOOL) pause forNodeTree:(id)parentNode shouldIncludeParentNode:(BOOL)includeParent
{
if(includeParent)
{
(pause) ? [parentNode pauseSchedulerAndActions] : [parentNode resumeSchedulerAndActions];
}
for( CCNode *cnode in [parentNode children] )
{
(pause) ? [cnode pauseSchedulerAndActions] : [cnode resumeSchedulerAndActions];
if(cnode.children.count > 0)
{
[self pauseSchedulerAndActions:pause forNodeTree:cnode shouldIncludeParentNode:NO]; // on recurse, don't process parent again
}
}
}
so in your case you could try calling this method and passing in your gameLayer
Try [[CCDirector sharedDirector] pause]; It should pause all animations and movements.

EXC BAD ACCESS With Cocos2d pushscene that works for blank scene

So I am trying to do a levelup screen with pushscene/popscene. The pushscene works when it's a blank scene, but not when it's my scene that I want. My scene/layer loads completely with all images and text displaying their exact correct content. After all of the images load there is a EXC BAD ACCESS that doesn't seem to be linked to any particular message being sent. Any help or further diagnostic tests would be appreciated.
I have a version where I commented out the sprites and labels and it still crashes. Is there something big that I'm missing?
EDIT: I've added the [self = [super init]] and [super onEnter] methods and still same problem. It's something else. Any ideas?
EDITEDIT: I think this has something to do with the optionsArray I'm using, not sure what objects need to be retained. The array is a CCArray and contains NSDictionaries of differing capacities
#import "LevelupLayer.h"
#import "GameManager.h"
#implementation LevelupLayer
#synthesize optionsArray,spritesArray;
#synthesize confirmLabel;
#synthesize counter;
+(id) scene {
CCScene *scene = [CCScene node];
CCLayer* layer = [LevelupLayer node];
[scene addChild:layer];
return scene;
}
-(void)onEnter
{
counter = 1; // for debugging
//Detemine what levelups are possible
GameManager* gm = [GameManager sharedManager]; //GameManager is a helper that oversees communication between layers and plists
optionsArray = [gm possibleLevelups]; //Access plist and formats data into expected format
[optionsArray retain];
int numPossibilities = [optionsArray count];
//Build Levelup layer based on possible options
CGSize size = [[CCDirector sharedDirector] winSize];
//float positionIncrement = (size.width / numPossibilities) - ((size.width/numPossibilities) * 0.5);
float positionIncrement = (size.width / numPossibilities);
float stripWidth = size.width / numPossibilities;
for (int i = 0; i < numPossibilities; i++) {
int slot = i+1;
NSDictionary* optionDict = [optionsArray objectAtIndex:i];
NSString* name = [optionDict objectForKey:#"name"];
NSString* title = [optionDict objectForKey:#"title"];
NSString* description = [optionDict objectForKey:#"description"];
// Add the sprite
CCSprite* optionSpite = [CCSprite spriteWithSpriteFrameName:[NSString stringWithFormat:#"%#.png",name]];
[self addChild:optionSpite];
[spritesArray addObject: optionSpite];
optionSpite.position = CGPointMake(slot * positionIncrement, size.height*0.60);
[optionSpite setAnchorPoint:CGPointMake(0.5f, 0.5f)];
// Add the description
CCLabelBMFont *optionDescription = [CCLabelBMFont labelWithString:description fntFile:#"bodyFont.fnt" width:stripWidth alignment:kCCTextAlignmentCenter];
[self addChild:optionDescription];
optionDescription.position = CGPointMake(slot * positionIncrement, size.height*0.30);
[optionDescription setAnchorPoint:CGPointMake(0.5f, 0.5f)];
// Add the title
CCLabelBMFont *optionTitle = [CCLabelBMFont labelWithString:title fntFile:#"titleFont.fnt" width:stripWidth alignment:kCCTextAlignmentCenter];
[self addChild:optionTitle];
optionTitle.position = CGPointMake(slot * positionIncrement, size.height*0.90);
[optionTitle setAnchorPoint:CGPointMake(0.5f, 0.5f)];
}
[self scheduleUpdate]; //Update only prints counter to see how many frames it lasts
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
-(void) update:(ccTime)delta
{
CCLOG(#"counter: %d",counter);
counter++;
}
-(void) onExit
{
[optionsArray release];
}
#end
`
i don't see any [super onEnter]; or [super init] or anything like that ...that's your problem
First add [super onEnter] on the first line in onEnter.
Second add an init method like this:
-(id)init{
if (self=[super init]){}
}
Third add [super onExit] at the end of your onExit method
I figured it out, it wasn't anything to do with the code I posted, so sorry about that. It was just a stupid release call to a non-retained array. Retained the array previously and it worked fine. Sorry about crying wolf

Animate CCSprite

I am newer to cocos2d and preparing my demo game. I am moving a sprite from right to left using just a single image like a bird image moving from left to right. But I want to animate that sprite through various images so that it may look like a flying bird. I don't know how to accomplish that.
Here is my code:
CCSprite *target = [CCSprite spriteWithFile:#"Target.png" rect:CGRectMake(0, 0, 27, 40)]
id actionMove = [CCMoveTo actionWithDuration:actualDuration position:ccp(-target.contentSize.width/2, actualY)];
id actionMoveDone = [CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)];
[target runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
For Animating particular sprite You would require sprite-sheet to be there in your Resource. You can create the Sprite-sheet from eitherTexture Packer OR Zwoptex Tools which I normally use.
Next you can implement the Below Code
CCSpriteBatchNode *sheet = [CCSpriteBatchNode batchNodeWithFile:#"drawing1-i3.png"]; // Png File Name
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"drawing1-i3.plist"]; // Plist File Name
[self addChild:sheet];
//Generating the Animation
NSMutableArray *arr_anim =[NSMutableArray array];
for(int i=1; i<30; i++) // i< number of frames in the plist File Name
{
NSString *str_fileNm = [NSString stringWithFormat:#"drawing1%d.png",i];
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:str_fileNm];
[arr_anim addObject:frame];
}
CCSprite *startAnim = [CCSprite spriteWithSpriteFrameName:#"drawing11.png"];
[startAnim setPosition:ccp(150,150)];
[self addChild:startAnim];
//Starting the Animation
CCAnimation *animation = [CCAnimation animationWithFrames:arr_anim delay:0.15f];
// id action =[CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation restoreOriginalFrame:YES]];
id action =[CCAnimate actionWithAnimation:animation restoreOriginalFrame:NO];
[startAnim runAction:action];
I think it would help you for creating the Animations.
Use the CCAnimation class.
In particular, use methods like animationWithFrames: and supply your images as an array.

Animations are not working properly(Cocos2d)

My problem is that animation are not working properly during movements of sprite.
Below is the code which i'm using
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
[selSprite resumeSchedulerAndActions];
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self selectSpriteForTouch:touchLocation];
return TRUE;
}
- (void)selectSpriteForTouch:(CGPoint)touchLocation
{
CCSprite * newSprite = nil;
for (CCSprite *sprite in movableSprite) {
if (CGRectContainsPoint(sprite.boundingBox, touchLocation)) {
newSprite = sprite;
break;
}
}
if (newSprite != selSprite) {
[selSprite stopAllActions];
selSprite = newSprite;
_MoveableSpritetouch = TRUE;
}
if(_MoveableSpritetouch==TRUE)
{
movement=0;
CGRect selRect=CGRectMake((SpriteX)-20.0,(SpriteY)-20.0,40.0,40.0);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
}
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if(gameState == TRUE){
CGPoint point = [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
if (moveDifference.x>0)
{
selSprite.flipX = YES;
}
else {
selSprite.flipX = NO;
}
[selSprite setPosition:point];
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
movement=0;
if(selSprite==MarshallCar)
{
[selSprite setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"mcar_idle.png"]];
}
[selSprite pauseSchedulerAndActions];
}
The animation frames for movement are not playing every time during movements sometimes it plays or sometimes not. It plays properly when you touch and move your sprite for the first time but if touch another sprite and then again move the previous sprite the animations for movement won't play.
Is anyone having any idea why this is happening?
Please tell me the proper code for removing this bug.
Thanks!!!
I believe your problem is the if/else if construct:
if (_MoveableSpritetouch==TRUE)
{
CGRect selRect = CGRectMake(SpriteX - 20, SpriteY - 20, 40, 40);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
else if(...)
{
...
[selSprite runAction:walkMarshallCar];
}
}
If you don't see it right away: if the touch location is inside the selRect, you call stopAllActions on the selected (new) sprite and do nothing else. Only if the touch location is not within that rectangle you'll run the animation action.
I think the "in rectangle" check is superfluous since you've already called stopAllActions anyway a few lines above.
Allow me a few general remarks about your code:
The method "selectSpriteForTouch" tells me that you're selecting a new sprite. The function does that. But it does not advertise playing an animation. You might want to refactor this out to a seperate "playAnimationOnSelectedSprite" method.
You wrote 20.0 and 40.0 several times. This means you're actually passing a double (8 bytes floating point data type) to a CGPoint which takes floats (4 bytes floating point). Strictly speaking use either 20.0f with the suffixed "f" to denote it as a floating point data type, or use integers since you don't use the floating point part.
Why you put (SpriteX) in brackets is not clear to me, if you want to enhance readability you'll achieve more by adding spaces after commas and operands.
In Objective-C, use YES and NO macros instead of TRUE and FALSE.
The bool variable _MoveableSpritetouch seems superfluous, unless needed someplace else. In any case you should move the following if(_MoveableSpritetouch==TRUE) block to where you set the _MoveableSpritetouch variable to TRUE because it just makes your code harder to read by setting a variable, leaving the code block you were in ( if(selSprite != newSprite) ) just to jump into another block of code ( if(_MoveableSpritetouch==TRUE) ) that you already know you're going to run anyway.
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
I have added [selSprite stopAllActions];
and it started working correctly because in the touch ended method i was pausing the actions
but not resuming them so when i touch the sprite for the second time it was not playing animation because the action was paused.

cocos2d scene retaining issue

There is a scene in my application which has only two labels and a menu item. When I load this scene using replaceScene method it stays for 3-4 seconds and then gets disappeared or released. I want to keep it until cancel button is pressed. How can I do it? code is:
#implementation MyLayer
+ (id)myScene {
CCScene *aScene = [CCScene node];
MYLayer *myLayer = [MyLayer node];
[aScene addChild:myLayer];
return aScene;
}
- (id) init {
if (self = [super init]) {
//labels and menu here
}
return self;
}
And I am calling it from another scene like this:
[[CCDirector sharedDirector] replaceScene: [MyLayer myScene]];
Maybe the problem is that it's your first scene. Then you should use runWithScene method of CCDirector.
did you try replacing that scene with a "empty" init function to see if it still releases itself? It might be because of the amount of textures you are putting into memory
I did have sort of similar problems before because the images used in the new scene is too big and got auto purged by my app delegate, thus returning me an empty scene sometimes