I am making application which is crashing giving the above warning in console.
There are two sprites.
Train=[CCSprite spriteWithFile:#"train.png"];
[self addChild:train z:0 tag:1];
AlphaImage=[CCSprite node];
AlphaImage.position=ccp(245,155);
[Train addChild:AlphaImage z:1 tag:2];
In other method I am initializing AlphaImage using animation.
CCSpriteFrameCache *frameCache1 =[CCSpriteFrameCache sharedSpriteFrameCache];
[frameCache1 addSpriteFramesWithFile:plist1];
CCSpriteBatchNode *danceSheet1 = [CCSpriteBatchNode batchNodeWithFile:png1];
[self addChild:danceSheet1];
NSMutableArray *animFrames1 = [NSMutableArray arrayWithCapacity:frame[x]];
for(int i = 1; i < frame[x]+1; i++) {
NSString *namef1=[NSString stringWithFormat:#"%#%i.png",alpha1,i];
CCSpriteFrame *frame1 = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:namef1];
[animFrames1 addObject:frame1];
}
CCAnimation *anim1 = [CCAnimation animationWithFrames:animFrames1 delay:0.3f];
CCAnimate *animN1 = [CCAnimate actionWithAnimation:anim1];
CCRepeatForever *repeat1 = [CCRepeatForever actionWithAction:animN1];
[AlphaImage runAction:repeat1];
I have used zwoptex to create plist and Texture atlas.
As train goes and comes back the plist and Texture atlas for animation changes. But after 4 or 5 times application is crashing. I have also deallcated all frames and texture before new animation comes to AlphaImage.i have used this:
[CCSpriteFrameCache purgeSharedSpriteFrameCache];
[CCTextureCache purgeSharedTextureCache];
I am using hd images for the application.I have gone through many documents, they have suggested these:
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
But in console it is not showing anything about removal of frames using these lines.
Is there something that I am doing wrong?
Where are you running [[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames]; this statement run it background thread using performSelectorInBackground method of NSObject.
Try to release an array after animation ends.
//This may be the reason.
[animFrames1 release];
animFrames1 = nil;
Related
I am looking to do a basic animation from a spritesheet. I got most my formatting/code from a tutorial I was following online, however it only did one directional motion. Basically I am looking to have 4 different animations that cause my sprite to "walk" in the direction of the touch with the appropriate animation. I've looked at many posts and tutorials but none of them seem to make the concept any more clear nor point out my errors.
I have 4 sets of 4 animations. A set for each cardinal direction (labeled Up, Down, Left, Right).
My sprite sheet is set up like so:
Note: There is also a *plist file that was automatically generated based on these individual files
[player1.png] [player2.png] [player3.png] [player4.png] //Walking down animations
[player5.png] [player6.png] [player7.png] [player8.png] //Walking left animations
[player9.png] [player10.png] [player11.png] [player12.png] //Walking right animations
[player13.png] [player14.png] [player15.png] [player16.png] //Walking up animations
This code that attempts to make these Actions in my init function in the HelloWorldLayer:
Note: This block of code is where it crashes!! I am not entirely sure where in here it does, but the error info is below as well!
//Store the sprite info in cahce
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"player.plist"];
//Load the spritesheet into a BatchNode
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"player.png"];
[self addChild:spriteSheet];
//Create frame arrays and store the appropriate .png sets
NSMutableArray *walkPlayerRightFrames = [NSMutableArray array];
NSMutableArray *walkPlayerLeftFrames = [NSMutableArray array];
NSMutableArray *walkPlayerUpFrames = [NSMutableArray array];
NSMutableArray *walkPlayerDownFrames = [NSMutableArray array];
for (int i=1; i<=4; i++)
{
[walkPlayerDownFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"player%d.png", i]]];
[walkPlayerLeftFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"player%d.png", i+4]]];
[walkPlayerRightFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"player%d.png", i+8]]];
[walkPlayerUpFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"player%d.png", i+12]]];
}
//These are of type "CCAnimation" and are declared in the header with appropriate #property stuff
_walkPlayerUp = [CCAnimation
animationWithSpriteFrames:walkPlayerUpFrames delay:0.1f];
_walkPlayerRight = [CCAnimation
animationWithSpriteFrames:walkPlayerRightFrames delay:0.1f];
_walkPlayerLeft = [CCAnimation
animationWithSpriteFrames:walkPlayerLeftFrames delay:0.1f];
_walkPlayerDown = [CCAnimation
animationWithSpriteFrames:walkPlayerDownFrames delay:0.1f];
//Declare a starting image and starting position
self.player = [CCSprite spriteWithSpriteFrameName:#"player1.png"];
self.player.position = ccp(100, 100);
//Set an action for when the player runs
[self.player runAction:self.walkAction];
//Add player to the spriteSheet
[spriteSheet addChild:self.player];
self.touchEnabled = YES;
Error Info:
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Argument must be non-nil'
*** First throw call stack:
(0x250d012 0x1eeae7e 0x250ce78 0x160b665 0x5c2a0 0xdf42f 0x59495 0xde9e2 0xe16f1 0x1efe663 0xbfa29 0xbe07a 0x5bfb4 0x32272 0xb44b8 0x15b4fb4 0x15ecb1a 0xb4c4c 0xddbaf 0xb5392 0xb8829 0xb0a2dd 0x1efe6b0 0x3cafc0 0x3bf33c 0x3caeaf 0xba92bd 0xaf1b56 0xaf066f 0xaf0589 0xaef7e4 0xaef61e 0xaf03d9 0xaf32d2 0xb9d99c 0xaea574 0xaea76f 0xaea905 0xaf3917 0xde183 0xab7157 0xab7747 0xab894b 0xac9cb5 0xacabeb 0xabc698 0x3215df9 0x3215ad0 0x2482bf5 0x2482962 0x24b3bb6 0x24b2f44 0x24b2e1b 0xab817a 0xab9ffc 0xdd9b6 0x1f35 0x1)
libc++abi.dylib: terminate called throwing an exception
Well, this was silly of me. Luckily I was doing the SpriteSheet manipulation correctly.
The following line needed remove:
[self.player runAction:self.walkAction];
I don't initialize the CCAction walkAction until later when I receive a touch on the screen, so this line was crashing because self.walkAction was nil by default.
This is my implementation to create an animation to an enemy that is inserted in the scene.
CCSpriteFrameCache *cache = [CCSpriteFrameCache sharedSpriteFrameCache];
CCAnimation *animation = [CCAnimation animation];
[animation addSpriteFrame:[cache spriteFrameByName:#"enemy_1.png"]];
[animation addSpriteFrame:[cache spriteFrameByName:#"enemy_2.png"]];
animation.delayPerUnit = 0.1;
[_enemy runAction:
[CCRepeatForever actionWithAction:
[CCAnimate actionWithAnimation:animation]]];
This is just one example of how to realize an animation.
For example, if I have a "Boss" made up of several parts, I would like to make it very quaint with animations in different parts of the body.
Is there a way to create animations smoother than the continuous replacement of images?
Computationally, this is the best you can do, or are there more efficient techniques?
Thanks for the help
Loading multiple images, one after another is the only way to 'animate' within cocos2d. Of course for various effects that can be mathematically defined you can use functions that are provided within the framework (like rotating, resizing, moving or physics based animation such as falling or bouncing)
If your artists provide a clean spritesheet with fluid frame by frame animation you'd be surprised by how smooth your animation can look.
//if you are using spritesheet then use this...for loading or sprites in your game...
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"walkcycle.plist"] ;
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"walkcycle.png"];
[heroWorldLayer addChild:spriteSheet];
NSMutableArray *enemyFrames = [NSMutableArray array];
for(int i = 1; i <= 11; ++i) {
CCSpriteFrame *frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"Run_Anim00%02d.png", i]];
[enemyFrames addObject:frame];
}
id enemyAnim = [CCAnimation animationWithFrames:enemyFrames delay:1.0/22.0];
id enemyAnimate = [CCAnimate actionWithAnimation:enemyAnim restoreOriginalFrame:NO];
id _runAction = [CCRepeatForever actionWithAction:enemyAnimate];
[_enemy runAction:_runAction];
I created a simple, 2 frame sprite animation based off a few tutorials that I found across the web. I took my 2 images, created a sprite sheet with plist and png file, and incorporated them into my code as seen below. This setup worked fine in Cocos2d V 1.0.1. I just upgraded my project to 2.0rc0a and now my app crashes with the following error at the point where it should switch from the first frame to the second: 'CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode'
I looked at this SO question, but I'm not sure if it's the same thing that I'm doing wrong, and because I'm still very new to Cocos2d, am unsure how to correctly adjust my code. Is this something that was changed in 2.0 that I didn't see in the notes, a bug that I should report, or just incorrect coding on my part? I still have a copy of the 1.0.1 project with identical code and the animation works correctly.
//CHAMELEON
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"front page/mainchameleon.plist"];
mainChameleon = [CCSpriteBatchNode batchNodeWithFile:#"front page/mainchameleon.png"];
[self addChild:mainChameleon z:7];
NSMutableArray *chameleonFrames = [NSMutableArray array];
for (int i = 1; i <= 2; ++i)
{
[chameleonFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"chameleon%d.png", i]]];
}
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonFrames delay:0.3f];
chameleon = [CCSprite spriteWithSpriteFrameName:#"chameleon1.png"];
CCAnimate *chameleonAction = [CCAnimate actionWithAnimation:mouthAnim];
CCDelayTime *chameleonDelay = [CCDelayTime actionWithDuration:10];
CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAction, chameleonDelay, nil]];
[chameleon runAction:chameleonRepeat];
[mainChameleon addChild:chameleon];
If I comment out chameleon = [CCSprite spriteWithSpriteFrameName:#"chameleon1.png"]; then the app doesn't crash, but the chameleon doesn't appear at all, as would be expected with how the code is currently written.
Or, if I comment out [chameleon runAction:chameleonRepeat]; then the chameleon appears showing the frame, chameleon1.png, but obviously doesn't go through the animation.
OK, so to make me even more confused because I'm definitely missing something, I tried changing the bottom portion of the code to this and the animation goes from frame 1, to frame 2, then stays on 2 indefinitely. However, if I make the delay anything longer than 1.0, I receive the same error I was getting before. If I re-include chameleonDelay prior to the action without the repeat forever statement, I also get the same crash. It appears that the app crashes if it has to wait longer than 1 second to perform the switch. What I need is for frame 1 to sit for a while (10 seconds) then switch to frame 2 for 0.3 seconds, then switch back to frame 1 and sit for a while again.
Attempted code #2:
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonFrames delay:0.3f]; //<--- maxes out at 1.0. Anything more causes crash
chameleon = [CCSprite spriteWithSpriteFrameName:#"chameleon1.png"];
CCAnimate *chameleonAction = [CCAnimate actionWithAnimation:mouthAnim];
[chameleon runAction:chameleonAction];
[mainChameleon addChild:chameleon];
YvesLeBorg suggested using the restoreOriginalFrame statement, but that is deprecated in ver 2.0. I tried using
CCAnimation *mouthAnim = [CCAnimation animationWithAnimationFrames:chameleonFrames delayPerUnit:0.3f loops:5];
and get the error '-[CCSpriteFrame delayUnits]: unrecognized selector sent to instance'. I'm not sure why that isn't working or what else to try from here.
EDIT: So Now It's Working...But not as efficiently coded as I'd like:
New Code:
//CHAMELEON
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"front page/mainchameleon.plist"];
mainChameleon = [CCSpriteBatchNode batchNodeWithFile:#"front page/mainchameleon.png"];
[self addChild:mainChameleon z:7];
NSMutableArray *chameleonFrames = [NSMutableArray array];
//Frame 1 - closed mouth
[chameleonFrames addObject:[CCSpriteFrame frameWithTexture:mainChameleon.texture rect:CGRectMake(0, 124, 149, 122)]];
//Frame 2 - Open Mouth
[chameleonFrames addObject:[CCSpriteFrame frameWithTexture:mainChameleon.texture rect:CGRectMake(0, 0, 149, 122)]];
//Frame 1 - closed mouth
[chameleonFrames addObject:[CCSpriteFrame frameWithTexture:mainChameleon.texture rect:CGRectMake(0, 124, 149, 122)]];
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonFrames delay:0.9f];
chameleon = [CCSprite spriteWithTexture:mainChameleon.texture rect:CGRectMake(0,124,149,122)];
CCAnimate *chameleonAction = [CCAnimate actionWithAnimation:mouthAnim];
CCDelayTime *chameleonDelay = [CCDelayTime actionWithDuration:10];
CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAction, nil]];
[chameleon runAction:chameleonRepeat];
[mainChameleon addChild:chameleon];
I really liked the way that I was doing it in 1.0.1 because if I had 2 frames or 100 frames, I only had to make a small adjustment to the if statement. This way requires coding each individual frame which seems counter-intuitive to using a plist. If no one is able to provide a better solution, or "real" answer in the next few days, I will post this as an answer and accept it to close out the question.
Here's the code I ended up with that works correctly. Not sure why I had to make these changes, but there it is. (Some of the names have changed as I started a new project to fix this on, but the differences between my original code and this are apparent). To anyone else finding this thread, my original code was based on Ray Wenderlich's sprite sheet tutorial.
CCSpriteBatchNode *chameleonBN = [CCSpriteBatchNode batchNodeWithFile:#"chameleonimages.png"];
[self addChild:chameleonBN];
//ADDED the texture part to resolve: 'CCSprite: setTexture doesn't work when the sprite is rendered using a CCSpriteBatchNode'
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"chameleonplist.plist" texture:chameleonBN.texture];
NSMutableArray *chameleonframes = [NSMutableArray array];
for (int i = 1; i <= 2 ; i++)
{
[chameleonframes addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"chameleon%d.png", i]]];
}
CCAnimation *mouthAnim = [CCAnimation animationWithSpriteFrames:chameleonframes delay:0.9f];
//ADDED this sprite frame to resolve texture id assert error
CCSpriteFrame *frame = [CCSpriteFrame frameWithTexture:chameleonBN.texture rect:CGRectMake(0, 0, 149, 122)];
CCSprite *chameleon = [CCSprite spriteWithSpriteFrame:frame];
chameleon.position = ccp(512,384);
CCAnimate *chameleonAnimate = [CCAnimate actionWithAnimation:mouthAnim];
CCDelayTime *chameleonDelay = [CCDelayTime actionWithDuration:10];
CCDelayTime *chameleonDelay2 = [CCDelayTime actionWithDuration:0.1];//Had to use this to ge tthe mouth to close. Using restore original frame doesn't work for me.
CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAnimate, chameleonDelay2, nil]];
[chameleon runAction:chameleonRepeat];
[chameleonBN addChild:chameleon];
Not certain whether this is a 2.0 issue or not, but i notice you are using twice the chameleonDelay object in the same sequence. Is this really what you were trying to accomplish ie
delay,action,delay,delay,action,delay,delay,action .... etc.
try this to see if it works :
CCRepeatForever *chameleonRepeat = [CCRepeatForever actionWithAction:[CCSequence actions:chameleonDelay, chameleonAction, nil]];
Hey, I'm having a performance problem with CALayers in a layer backed NSView inside an NSScrollView. I've got a scroll view that I populate with a bunch of CALayer instances, stacked one on top of the next. Right now all I am doing is putting a border around them so I can see them. There is nothing else in the layer.
Performance seems fine until I have around 1500 or so in the scroll view. When I put 1500 in, the performance is great as I scroll down the list, until I get to around item 1000. Then very suddenly the app starts to hang. It's not a gradual slowdown which is what I would expect if it was just reaching it's capacity. It's like the app hits a brick wall.
When I call CGContextFillRect in the draw method of the layers, the slowdown happens around item 300. I'm assuming this has something to do with maybe the video card memory filling up or something? Do I need to do something to free the resources of the CALayers when they are offscreen in my scroll view?
I've noticed that if I don't setNeedsDisplay on my layers, I can get to the end of 1500 items without slowdowns. This is not a solution however, as I have some custom drawing that I must perform in the layer. I'm not sure if that solves the problem, or just makes it show up with a greater number of items in the layer. Ideally I would like this to be fully scalable with thousands of items in the scroll view (within reason of course). Realistically, how many of these empty items should I expect to be able to display in this way?
#import "ShelfView.h"
#import <Quartz/Quartz.h>
#implementation ShelfView
- (void) awakeFromNib
{
CALayer *rootLayer = [CALayer layer];
rootLayer.layoutManager = self;
rootLayer.geometryFlipped = YES;
[self setLayer:rootLayer];
[self setWantsLayer:YES];
int numItemsOnShelf = 1500;
for(NSUInteger i = 0; i < numItemsOnShelf; i++) {
CALayer* shelfItem = [CALayer layer];
[shelfItem setBorderColor:CGColorCreateGenericRGB(1.0, 0.0, 0.0, 1.0)];
[shelfItem setBorderWidth:1];
[shelfItem setNeedsDisplay];
[rootLayer addSublayer:shelfItem];
}
[rootLayer setNeedsLayout];
}
- (void)layoutSublayersOfLayer:(CALayer *)layer
{
float y = 10;
int totalItems = (int)[[layer sublayers] count];
for(int i = 0; i < totalItems; i++)
{
CALayer* item = [[layer sublayers] objectAtIndex:i];
CGRect frame = [item frame];
frame.origin.x = self.frame.size.width / 2 - 200;
frame.origin.y = y;
frame.size.width = 400;
frame.size.height = 400;
[CATransaction begin];
[CATransaction setAnimationDuration:0.0];
[item setFrame:CGRectIntegral(frame)];
[CATransaction commit];
y += 410;
}
NSRect thisFrame = [self frame];
thisFrame.size.height = y;
if(thisFrame.size.height < self.superview.frame.size.height)
thisFrame.size.height = self.superview.frame.size.height;
[self setFrame:thisFrame];
}
- (BOOL) isFlipped
{
return YES;
}
#end
I found out it was because I was filling each layer with custom drawing, they seemed to all be cached as separate images, even though they shared a lot of common data, so I switched to just creating a dozen CALayers, filling their "contents" property, and adding them as sublayers to a main layer. This seemed to make things MUCH zippier.
In cocos2d, there used to be a TextureMgr method (Async) that let you load images for later use. Now when I use TextureMgr, it says it's undeclared. Is it deprecated? I'm on .99.5. If it's no longer usable, what replaces it? Is there anything that can do the same as this line?
[[TextureMgr sharedTextureMgr] addImageAsync:...NSString goes here... target:self selector:#selector(method:)];
take a look at CCTextureCache
http://www.cocos2d-iphone.org/api-ref/0.99.5/interface_c_c_texture_cache.html
maybe it is you are looking for.
This cache is used when your are creating any object weith a texture: sprite for example. And you can use it for precaching your images.
EDIT:
The CCTextureCache is used when your creating ang object with a texture, as i said - and so if the texture is already in cache the element creation is much more faster then if you would loading the texture first and then creating an object.
For example if you are writing the code like this:
id sprite = [CCSprite spriteWithFile: #"my-file.png"]
and the #"my-file.png" texture is not in cache it will be loaded first and it will take some time (much more then just creating an object).
If you are writing code like this:
id sprite1 = [CCSprite spriteWithFile: #"my-file.png"];
id sprite2 = [CCSprite spriteWithFile: #"my-file.png"];
Then sprite1 will be created slow and sprite2 much more faster because the texture is already in cache.
You can manuale add texture to cache
[[CCTextureCache sharedTextureCache] addImage: #"my-file.png"];
and the creating of all objects with this texture will be fast.
The common place in code when you have to precache textures is game loading or level package loading or level loading.
Also You can precache sounds if you need using them SimpleAudioEngine singleton
Andrew pretty much answered your question, I just want to give you a code segment about how to use CCTextureCache and CCSpriteFrameCache. Texture Cache load real textures/images and sprite frame cache loads the information regarding textures (if you are loading a Sprite sheet.)
Okay here's the sample code.
Here latestBurp1-1.pvr.ccz and burpParticles1-1.png are my sprite sheets and their information is in (same name).plist files.
In the below function I am loading textures and also spriteFrames (the info about textures).
Also take a look at pvr files and pvr.ccz file they load much faster than png.
-(void) loadBurpAnimation {
NSString* burpFile;
NSString* burpFilePVR;
NSString* burpParticlesFile;
NSString* burpParticlesFilePVR;
burpFile = #"latestBurp1-1.plist";
burpFilePVR = #"latestBurp1-1.pvr.ccz";
burpParticlesFile = #"burpParticles1-1.plist";
burpParticlesFilePVR = #"burpParticles1-1.png";
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpFile texture:[[CCTextureCache sharedTextureCache] addImage:burpFilePVR]];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpParticlesFile texture:[[CCTextureCache sharedTextureCache] addImage:burpParticlesFilePVR]];
NSMutableArray *burpFrames = [NSMutableArray array];
for(int i = 231; i <= 268; ++i) {
[burpFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"burp.%d.png", i]]];
}
burpAnim = [[CCAnimation alloc] initWithFrames:burpFrames delay:0.04f];
[burpFrames removeAllObjects];
//Burp Particles
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:burpParticlesFile];
NSMutableArray *burpParticlesFrames = [NSMutableArray array];
for(int i = 3; i <= 37; ++i) {
[burpParticlesFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"Burp_%05d.png", i]]];
}
burpParticlesAnim = [[CCAnimation alloc] initWithFrames:burpParticlesFrames delay:0.04f];
[burpParticlesFrames removeAllObjects];
}
I just gave you a lot of information so you might need to google some terms.
I think you are looking for [CCSpriteFrameCache sharedSpriteFrameCache]
http://www.cocos2d-iphone.org/api-ref/0.99.5/interface_c_c_sprite_frame_cache.html