removeSpriteFramesFromFile doesn't seem to work - iphone

I'm using CCScrollLayer. and I'm trying to prepare a level's texture at the stage select screen before level is started.
So, I made this screen as when I change level, then before level's textured that was prepared is supposed to be removed. But I don't think "removeSpriteFramesFromFile" method work well. because when I scroll the several layer, It suddenly call "Memory warning" and remove that textures at that late time then I expected.
-(void) prepareTexture:(NSNumber*)number
{
int _page = [number intValue];
if(loadingTexNum != 0 && (_page + 1) != loadingTexNum)
{
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromFile:[NSString stringWithFormat:#"L%d.plist", loadingTexNum]];
loadingTexNum = _page + 1;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:#"L%d.plist", loadingTexNum]];
}
if(loadingTexNum == 0 && (_page + 1) != loadingTexNum)
{
loadingTexNum = _page + 1;
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:#"L%d.plist", loadingTexNum]];
}
}
Thanks

I believe you misunderstood what that method does:
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFramesFromFile:#"file.plist"];
It loads the sprite frames in the plist, then removes the CCSpriteFrame objects in that plist from the cache - if they are cached. It does NOT remove the texture, only the CCSpriteFrame objects!
To remove a texture from the cache you need to call:
[[CCTextureCache sharedTextureCache] removeTexture:tex];
You should know that a CCSpriteFrame object is a relatively light-weight object that consumes at most 64 Bytes of memory. That is nothing compared to even a very small 32x32 texture with 16-bit color depth which uses 2048 Bytes of memory.

Related

why CCAnimation does not use CCSpriteBatchNode to print its images?

why CCSpriteBatchNode is not explicitly used with CCAnimation ? instead we use the following : (instead of adding each image to the batchNode and let the batchNode print those images, the code only uses spriteFrameByName) :
CCSpriteBatchNode *chapter2SpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"];
CCSprite *vikingSprite = [CCSprite spriteWithSpriteFrameName:#"sv_anim_1.png"];
[chapter2SpriteBatchNode addChild:vikingSprite];
// Animation example with a CCSpriteBatchNode
CCAnimation *exampleAnim = [CCAnimation animation];
[exampleAnim addFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"sv_anim_2.png"]];
Thanks for your answer
A batchNode is just a texture draw artefact ... has no knowledge whatsoever about what frames the texture contains, nor whether it embeds multiple 'logical' files in a single texture. You must create that association, typically by adding spriteFrame's to the spriteFrameCache, each spriteFrame providing metadata about a 'fragment' of the batchNode. Here is an example from one of my games:
-(void) setupIdleAnimation{
[self setupAnimations];
NSString* animationName = #"Idle";
NSString* framesFileName = [self getPlistFileNameForAction:animationName];
CCSpriteBatchNode *bn = [CCSpriteBatchNode batchNodeWithFile:[self getTextureFileNameForAction:animationName]];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:framesFileName texture:bn.texture];
// create array of frames for the animation
self.idleBatchNode=bn;
NSMutableArray *animFrames = [NSMutableArray array];
for(NSUInteger i = 1; i <= 8; ++i) {
NSString *sfn = [self getFrameNameForAnimation:animationName
andFrameNumber:i];
CCSpriteFrame *sf = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:sfn];
if(sf) {
[animFrames insertObject:sf atIndex:i-1];
} else {
CCLOGERROR(#"%#<setupIdleAnimation> : *** Sprite frame named [%#] not found in cache, bailing out.",self.class,sfn);
return;
}
}
CCAnimation *anim=[CCAnimation animationWithFrames:walkAnimFrames delay:ANIM_FRAME_DELAY];
[animFrames removeAllObjects];
anim.name=animationName;
self.idleAction = [CCRepeatForever
actionWithAction:[CCAnimate actionWithAnimation:anim
restoreOriginalFrame:NO]] ;
self.idleSprite = [CCSprite spriteWithSpriteFrameName:[self getFrameNameForAnimation:animationName
andFrameNumber:1]];
self.idleSprite.visible=NO;
[self.idleBatchNode addChild:self.idleSprite];
}
So i have prepared in a .plist the data that describes where each frame is located in the texture, and add these definitions to the frame cache. The batchNode per say is a container that will optimise rendering performance. In the above example it is well suited singe the texture embeds the idle sprites for 16 character classes that are often in view and idling simultaneously.
You can find a good introduction in this tutorial.

Memory warning on initializing multiple animations for a sprite

I am creating an app which has around 15 different animations. Some animations are played on button taps while some are played on certain event. As I am new to cocos2d I just cannot figure out how to initialize these animations. I have separate sprite sheets for each animation. What I have done is created a convenience class that has as a data member, a CCSprite object. And in the init function I have placed all the code to create animations. But as soon as the init function is called I get memory warning.
I even tried to initialize the animations only when the request for playing the animation is sent, but I still get the memory warning. Also if I do so the animation doesn't play until the whole sprite sheet is loaded.
Please check the following code for reference
- (void)initEntryAnimation
{
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"entry.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"entry.png"];
[self.sardarSprite addChild:spriteSheet];
NSMutableArray *entryFrames = [NSMutableArray array];
for(int i = 0; i < 77; i++) {
if([[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"enter in scene%04i.png", i+1]])
[entryFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"enter in scene%04i.png", i+1]]];
}
self.entryAnimation = [CCAnimation animationWithFrames:entryFrames delay:0.05f];
}
- (void)playEntryAnimation
{
[CCSpriteFrameCache purgeSharedSpriteFrameCache];
[self.sardarSprite runAction:[CCAnimate actionWithAnimation:self.entryAnimation restoreOriginalFrame:NO]];
}
I have created similar functions for all the 15 animations. I call all the initialization functions one after another. And the play functions are called whenever a request for the animation is sent.
I desperately need some expert advise. Any help would be highly appreciated.
The problem is the size of your sprite sheets. The sprite sheet file size (the size of the png) may be very small, but that is the size on disk. Once your app has loaded that into memory, it is stored uncompressed so that's 2048x2048x8 per sprite sheet.
You can reduce the colour depth to RGBA4444, for example which may help. See TexturePacker for more details. There are other tools available (I am not affiliated with TexturePacker in any way).
Other options you have are to reduce the colour depth further or to use one of the other formats available. You could also look at your animations and see if there is any way to break them down into more discrete components. For example, if you have an animation of a man running, perhaps instead of having the full screen frames, you could have one frame of the torso and then different leg animations and arm animations which you then use.
Without knowing the exact animation, it's difficult to advise.
Try adding NSAutoreleasePool like this
- (void)initEntryAnimation
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"entry.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"entry.png"];
[self.sardarSprite addChild:spriteSheet];
NSMutableArray *entryFrames = [NSMutableArray array];
for(int i = 0; i < 77; i++) {
if([[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"enter in scene%04i.png", i+1]])
[entryFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"enter in scene%04i.png", i+1]]];
}
self.entryAnimation = [CCAnimation animationWithFrames:entryFrames delay:0.05f];
[pool release];
}

cocos2d sprite frame cache unable to change

in my game i want to make several themes. (like doodle jump)
the frame names are the same in different themes and i think i just need to change the plist files.
in the Helper.m:
+(void) addCache {
ThemeTypes themeType = [Helper getCurrentThemeType];
if (themeType == ThemeTypeDefaultTheme) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"block.plist"];
}
else if (themeType == ThemeTypeJungleTheme) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"jungle1.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"jungle2.plist"];
}
else if (themeType == ThemeTypeXmasTheme) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"xmas1.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"xmas2.plist"];
}
else if (themeType == ThemeTypeWhiteTheme) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"blockwhite.plist"];
}
else if (themeType == ThemeTypeOuterSpaceTheme) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"outer1.plist"];
}
else if (themeType == ThemeTypeSkyTheme) {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sky1.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"sky2.plist"];
}
else {
}
}
+(void) purgeCache {
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
[CCSpriteFrameCache purgeSharedSpriteFrameCache];
}
+(void) refreshCache {
[Helper purgeCache];
[Helper addCache];
}
in appdelegate, i will call 'addcache', then in my ShopScene, when choosing a theme, 'refresh cache' will get called.
this code is in my BlockCache ('normal block' is a kind of block in game')
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"normal.png"];
batch = [CCSpriteBatchNode batchNodeWithTexture:frame.texture];
[self addChild:batch];
(note that #"normal.png" exist in every plist files. use the same name.
but after changing the theme, the 'block cache' still use the original images. however, the background etc uses the correct one (I use bg = [CCSprite spriteWithSpriteFrameName: ] method
UPDATE: after choosing the theme, i kill the game, and then relaunch it, it works fine. which means the call from Appdelegate is fine, but i can't change the cache during run time.
Sprite frames reference textures. The textures are cached in CCTextureCache. You probably also need to remove the textures from the CCTextureCache.
And if you still have sprites on screen which are making use of texture A but you want them to make use of texture B, you have these options:
send the setTexture: message with the new texture to each sprite
remove the old sprites and add new ones
reload the current scene
Note that calling [CCSpriteFrameCache purgeSharedSpriteFrameCache]; is superfluous in any case. You don't need to deallocate the singleton, just purge whatever it is caching.
You can find a working solution for texture unloading and replacing the texture a sprite uses in the code for my Cocos2D Webcam Viewer tutorial.

Cocos2d SpriteSheet Animation

everywhere i look i find code on how to run an animation using file created from zwoptex, but it is using deprecated code. I can't find code on how to run the animation using CCSpriteBatchNode.
Any suggestions?
This is the way I do animations using files created from zwoptex, but I am using CCSpriteFrameCache. I don't get any warnings, so I suppose this code is not deprecated:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"frameSheet.plist"];
NSMutableArray* frames = [NSMutableArray array];
for(int i = 1; i <= numberOfFrames; ++i) {
[frames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"frame-%03d.png", i]]];
}
CCAnimation* anim = [CCAnimation animationWithFrames:frames delay:0.3f];
Texture file is named frameSheet.png, plist file is frameSheet.plist, and the composing images are named frame-001.png ... frame-100.png.
Let me know if this helps you.
EDIT:
strangely, for me animationWithFrames:frames is not deprecated:
,
it says "from 0.99.5" and I have no issues with my animations...

Frame Rate per Second (FPS) starts to gdecrease in Coco2d game iPhone?

i have three simple images each for 2 hens (6 images) which I am trying to animate (a hen walking) using a very good tutorial by Ray Wenderlich:
http://www.raywenderlich.com/1271/how-to-use-animations-and-sprite-sheets-in-cocos2d
The animation works fine when I start the game but after 2-3 mins the Frame Rate begins to drop and slowly it drops to below 10 and the application hangs.. btw I am using iPhone 3G with iOS 4.1 ... can that be the reason for the FPS drop or just the iPhone becomes idle after some time?
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"hen.plist"];
CCSpriteBatchNode *spriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"hen.png"];
[self addChild:spriteSheet];
// Load up the frames of our animation
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 3; ++i) {
[walkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"%d.png", i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithFrames:walkAnimFrames delay:0.1f];
self.bear = [CCSprite spriteWithSpriteFrameName:#"1.png"];
_bear.position = ccp(20,400);
self.walkAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnim restoreOriginalFrame:NO]];
[_bear runAction:_walkAction];
[spriteSheet addChild:_bear];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"monkey.plist"];
CCSpriteBatchNode *spriteSheetMonkey = [CCSpriteBatchNode batchNodeWithFile:#"monkey.png"];
[self addChild:spriteSheetMonkey];
NSMutableArray *walkAnimFramesMonkey = [NSMutableArray array];
for(int i = 1; i <= 3; ++i) {
[walkAnimFramesMonkey addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"%d.png", i]]];
}
CCAnimation *walkAnimMonkey = [CCAnimation animationWithFrames:walkAnimFramesMonkey delay:0.1f];
self.monkey = [CCSprite spriteWithSpriteFrameName:#"1.png"];
_monkey.position = ccp(40,80);
self.walkMonkey = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:walkAnimMonkey restoreOriginalFrame:NO]];
[_monkey runAction:_walkMonkey];
[spriteSheetMonkey addChild:_monkey];
float bearVelocity = 480.0/3.0;
CGPoint moveDifferenceHen = ccpSub(HenLoction, _bear.position);
float distanceToMoveHen = ccpLength(moveDifferenceHen);
float moveDurationHen = distanceToMoveHen / bearVelocity;
self.moveAction = [CCSequence actions:
[CCMoveTo actionWithDuration:moveDurationHen position:HenLoction],
[CCCallFunc actionWithTarget:self selector:#selector(bearMoveEnded)],
nil
];
[_bear runAction:_moveAction];
Sounds like you might have a memory leak. My advice would be to run the Leaks and ObjectAlloc Instruments on the device. Also, you should post the relevant code if you want more detailed assistance.
A memory leak seems most likely.
If that's not it then I'd look at things like data structures you're traversing, any kind of decision-tree-like elements you're building over time, that kind of thing. Anything where there isn't necessarily a memory leak but something might be legitimately getting big or complex, requiring increasing processing time.
If the app has to work through this several-to-many times a second, everything would slow down. It could be the kind of thing where it's getting twice as complicated or large ever few minutes, and the early doublings are still fast and easy to calculate. When it doubles from, say, 256 times as complex as the original to 512 times, the slowdown starts to become apparent.