Cocos2D CCTextureCache stays in memory - memory

I load images in a asynchronous way, for a scroller, using addImageAsync.
I start profiling, and saw some memory issue, images stayed in memory.
Then I decided to compare addImageAsync vs spriteWithFile, with loading and directly unloading images.
Once all the loadings are done, addImageAsync is using more memory.
In a loop that loads many images, if I use
CCSprite* sprite = [CCSprite spriteWithFile:previewPath];
NSLog(#"sprite created");
[[CCTextureCache sharedTextureCache] removeTexture:sprite.texture];
NSLog(#"texture removed");
sprite = NULL;
NSLog(#"sprite nulled");
I end up with 72.4MB
If I use:
[[CCTextureCache sharedTextureCache] addImageAsync:previewPath withBlock:^(CCTexture2D *tex) {
[[CCTextureCache sharedTextureCache] removeTexture:tex];
NSLog(#"texture removed");
[CCTextureCache purgeSharedTextureCache];
}];
I end up with 108.7MB
Is there a problem here, or do I do something wrong ?
(using Cocos2d V2)

Related

Sprite animation not working in NSThread method in Cocos2d

I created a Thread like the below one:
[NSThread detachNewThreadSelector:#selector(connectionFinishedThread) toTarget:self withObject:nil];
inside this method, i created one sprite and given animation for this sprite. Animation not visible.
My code inside the Thread method:
CCSprite *aniSprite = [CCSprite spriteWithSpriteFrameName:#"r_anim01.png"];
aniSprite.position = ccp(50, 50);
[self addChild:aniSprite z:22];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"r_anim1.plist"];
CCSpriteBatchNode *animSheet = [CCSpriteBatchNode batchNodeWithFile:#"r_anim1.png"];
[self addChild:animSheet];
NSMutableArray *animFrames = [NSMutableArray array];
for (int i=1; i<=6; i++) {
[animFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"r_anim1%d.png",i]]];
}
CCAnimation *anim = [CCAnimation animationWithSpriteFrames:animFrames delay:0.1f];
CCAction *spriteAction = [CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:anim]];
[sprite runAction:spriteAction];
Why it behaves like that?
Most changes to properties of CCNode classes must be done on the main thread. Cocos2D does not support multi-threading like Sprite Kit.
For example changing a sprite's texture from a background thread will certainly crash. But even subtle issues can occur because all properties are declared nonatomic.
The only way to use threads with cocos2d is to make sure whatever logic runs in the background thread does not directly change a node's properties. Ie doing some AI calculations in the background is fine, as long as AI actors aren't nodes but custom classes.
You should not try to manipulate cocos2d objects (e.g. CCNode derived object) from another thread. The container holding the object (CCLayer, CCScene, etc.) may be manipulating it at the same time and none of the typical concurrency mechanisms (mutexes) are in effect.
If you need your sprite to take some kind of update action every time the frame updates, should schedule the container to update and update the sprite then. It is very common to move sprites around, update their orientations, etc., during the CCScene update.

Crash when restarting a scene

So, here is my scene problem: I start from a Menu Scene, then go into the InGame Scene, and when the character is dead, I go to the Menu Scene again, all this using:
[[CCDirector sharedDirector] replaceScene:[MainMenu scene]];
and
[[CCDirector sharedDirector] replaceScene:[InGame scene]];
After losing the game, and trying to go back to the game, my SpriteSheet crashes with the error :
'CCSprite is not using the same texture id'
Here is how I init the animation :
- (void) initSprite:(NSString *)plist andTexture:(NSString *)texture_ {
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:plist];
spriteSheet = [CCSpriteBatchNode batchNodeWithFile:texture_];
NSMutableArray *walkAnimFrames = [NSMutableArray array];
for (int i=1; i<=12; i++) {
[walkAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"%d.png",i]]];
}
CCAnimation *walkAnim = [CCAnimation animationWithSpriteFrames:walkAnimFrames delay:0.05f];
texture = [CCSprite spriteWithSpriteFrameName:#"1.png"];
walkAction = [CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:walkAnim]];
[texture runAction:walkAction];
texture.position = position;
texture.tag = HeroType;
[spriteSheet addChild:texture];
[self addChild:spriteSheet];
}
The crash occurs when I add the texture to the spriteSheet:
[spriteSheet addChild:texture];
I believe the problem comes from the deallocation of the texture..
I don't use ARC.
You probably have a "1.png" in the cache that corresponds to another animation that was created before quit-restart sequence. Maybe you want to purge the sprite frame cache (and possibly a whole lot of other things) prior to restarting.
I completely avoid "1.png" through 'NNNN.png' since in all likelihood all your animations will have them. Instead i use things like :
walk_classKey_upNNNN.png {up,down,left,right,idle,jump ... and any other map stance i need)
fight_classKey_strikeNNNN.png {strike,hurt,dead ... for example)
classKey is {fighter,rogue, ... etc ... whichever unique soldier type I have)
and i name my plists/textures the same
walk_fighter_up-hd.plist (using texture packer, the plist embeds the texture name).
fight_rogue_cheapShot-hd.plist (cheapShot is one of my rogue's skill in my current game).

CCSprite not showing Background Image

I am facing an unknown error since from yesterday. I am creating CCSprites or CCMenuItemImage but it set black background instead of background image. Following is my code, I know its fine because I used it before many times.
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"challenge_screen.plist"];
CCSprite *bg = [CCSprite spriteWithFile:#"ads.png"];
[bg setPosition:background.position];
// [bg setContentSize:CGSizeMake(100, 100)];
[self addChild:bg z:1000];
//CGSize windowSize = [[CCDirector sharedDirector] winSize];
CCMenuItemImage *coinMenuItem = [[CCMenuItemImage alloc] initWithNormalSprite:[CCSprite spriteWithSpriteFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"coin.png"]] selectedSprite:nil disabledSprite:nil block:^(id sender)
{
NSLog(#"I am Tapped");
}];
coinMenuItem.position = ccp(100, 100);
CCMenu *mainMenu = [CCMenu menuWithItems:coinMenuItem, nil];
mainMenu.position = CGPointZero;
[self addChild:mainMenu];
Attached is screenshot.
Thanks in advance.
I am guessing that you are loading this sprite sheet (challenge_screen.plist and the associated texture file, which frequently is challenge_screen.png or challenge_screen.pvr.*) in a color mode that doesn't have transparency.
First, make sure that the associated texture file shows transparency itself. Maybe something messed with this particular texture.
Once you checked that, if the associated texture is .PNG. then you have to set the texture loading format in code like this. You have to set the texture format before loading the texture itself (the texture loads as a side effect of adding the SpriteFrames to the cache).
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"challenge_screen.plist"];`
...
You can also try the kCCTexture2DPixelFormat_RGBA8888 mode if RGBA444 produces banding with your graphics and if you are good regarding free memory.
On the other hand, if the texture is a PVR.*, then the format in which the texture loads is embedded in the file, and setting the texture format in code doesn't make a difference. You will then need to regenerate your sprite sheet using the appropriate format (through TexturePacker or similar).
Is your background in the sprite sheet? If so try:
CCSprite *bg = [CCSprite spriteWithSpriteFrameName:#"ads.png"];
If it is the menu item and you know the code works, it must be an asset issue.

""CCSprite is not using the same texture id"" workaround?

I am animating a whole body through SpriteSheets with CCSpriteBatchNode and CCSpriteFrameCache. Now user can add his own pic to that body which when i try to addChild to Spritesheet crashes with error "CCSprite is not using the same texture id"
Now i know the face CCSprite was not in that cache/texture(it was created through texturepacker) and the crash was normal but i wanted to know if there was a workaround to this as i have to add a face to that body through user interaction and animate that body. And by far using spritesheets is the best option for animation. anyone??
In this case what you can do is You take picture of user , then you make texture from user's image .
Then Add that texture to the CCTextureCache . Now you have texture of user image. Now you can use that texture in animation.
Make Texture from Sprite(You can make sprite from user image)
CCSprite *spr = nil;//your sprite
CCRenderTexture* renderTexture = [CCRenderTexture renderTextureWithWidth:spr.contentSize.width height:spr.contentSize.height];
spr.anchorPoint = ccp(0, 0);
spr.position = ccp(0, 0);
[renderTexture addChild:spr];
[renderTexture begin];
[spr draw]; // or [spr visit];
[renderTexture end];
CCTexture2D *result = renderTexture.sprite.texture;
Add that Texture in to Texture Cache.
[[CCTextureCache sharedTextureCache] addTexture]
When you create a CCSpriteBatchNode it is linked to a single texture. This is the point of a CCSpriteBatchNode: to draw different sprites that use the same texture to reduce OpenGL draw calls and increase efficiency.
The easiest workaround (if you have not reached a performance-critical point) would be to use a regular CCLayer instead of a CCSpriteBatchNode.
If you still want to add different CCSprites (say, the body, the limbs and the head of your character) to the same CCSpriteBatchNode, the you need to build a single sprite sheet (or texture pack) which contains all the body parts that you need to add to the CCSpriteBatchNode. This single sprite sheet will be the only one that the CCSpriteBatchNode will use. You won't be able to add CCSprites that are not using this sprite sheet.
You can not Manually add CCSprite to SpriteSheets. Because When you create animated Sprite using texturepacker then i hope that you know it also created with .plist file and it loas image from it with its size.
When you add manually CCSprite then it is nor found from SpriteFramesWithFile. may be you got error.
another way for add animated CCSprite without use of texturepacker
CCSprite *dog = [CCSprite spriteWithFile:#"dog1.gif"];
dog.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:dog z:2];
NSMutableArray *animFrames = [NSMutableArray array];
for( int i=1;i<=5;i++)
{
NSString* file = [NSString stringWithFormat:#"dog%d.gif", i];
CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:file];
CGSize texSize = texture.contentSize;
CGRect texRect = CGRectMake(0, 0, texSize.width, texSize.height);
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect:texRect];
[animFrames addObject:frame];
}
CCAnimation * animation = [CCAnimation animationWithSpriteFrames:animFrames];
animation.delayPerUnit = 0.07f;
animation.restoreOriginalFrame = YES;
CCAnimate *animAction = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:animation]];
[dog runAction:animAction];
Above code is just describe that how can you add animated CCSprite without use of texturepacker
Here you can also change array of Images so you may be add manually image also.

optimize animation in cocos2d failed

When I didn't optimize animation,it worked well.But when I optimized my animation,the program crashed.And Xcode log said:
Assertion failure in -[CCSpriteBatchNode addChild:z:tag:], /Users/hanpengbo/Documents/Xcode/cocos2d_helloWorld/cocos2d_helloWorld/libs/coco‌​s2d/CCSpriteBatchNode.m:183
in CCSpriteBatchNode.m:183,there is
NSAssert( child.texture.name == textureAtlas_.texture.name, #"CCSprite is not using the same texture id");
here is my code
// cache
CCSpriteFrameCache *cache=[CCSpriteFrameCache sharedSpriteFrameCache];
[cache addSpriteFramesWithFile:#"birdAtlas.plist"];
// frame array
NSMutableArray *framesArray=[NSMutableArray array];
for (int i=1; i<10; i++) {
NSString *frameName=[NSString stringWithFormat:#"bird%d.png", i];
id frameObject=[cache spriteFrameByName:frameName];
[framesArray addObject:frameObject];
}
// animation object
id animObject=[CCAnimation animationWithFrames:framesArray delay:0.1];
// animation action
id animAction=[CCAnimate actionWithAnimation:animObject restoreOriginalFrame:NO];
animAction=[CCRepeatForever actionWithAction:animAction];
// sprite
cocosGuy = [CCSprite spriteWithFile: #"Icon.png"];//cocosGuy is CCSprite,declared earler
cocosGuy.position = ccp( 200, 300 );
// batchNode
CCSpriteBatchNode *batchNode=[CCSpriteBatchNode batchNodeWithFile:#"birdAtlas.png"];
[self addChild:batchNode];
[batchNode addChild:cocosGuy];
[cocosGuy runAction:animAction];
UPDATE:
here is the corrected code,and it works well
// batchNode
CCSpriteBatchNode *batchNode=[CCSpriteBatchNode batchNodeWithFile:#"birdAtlas.png"];
[cocosGuy setTexture:[batchNode texture]];
[self addChild:batchNode];
[batchNode addChild:cocosGuy];
For this to work, your Icon.png texture should be in the birdAtlas.png texture, with appropriate declaration in the .plist. Batchnodes 1) are created with one texture (and only one), and 2) only accept as children sprites that are from the SAME texture.
... and , i dont know your intent, but typically you would have
CCSprite *cocosGuy = [CCSprite spriteWithSpriteFrame:[cache spriteFrameByName:#"bird1.png"];
in that case, i think the batch node add will work.
... and , not sure using a batch node will be of any consequence for an animation, if the texture only contains the animation frames for that one animation. Frames are displayed one at a time, so i dont think you will benefit from the batched draw call.

Resources