Switch the cocos2d retina display background in runtime. - background

I've a background in my app, but clicking on a button should change it and use a different one.
I cannot add all of them in the sharedTextureCache because each one has a size of >16MB in the cache, and I've 30 different backgrounds.
What's the best way for switching the background without a loading time? I don't want the user to wait when clicking that button.
Thanks

remove all unused data. or texture...
[[CCDirector sharedDirector] purgeCachedData];
[[CCTextureCache sharedTextureCache] removeAllTextures];
[CCTextureCache purgeSharedTextureCache];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
[CCSpriteFrameCache purgeSharedSpriteFrameCache];

If you know what background will be shown next, you can preload it beforehand. Each retina device can use about 100 megabytes of memory (about 5 spritesheets 2048x2048). In this case you begin to receive memory warnings but app will work stable. All preloads you can make asynchronously to shared texture cache. Just don't forget to clean up unused textures by calling
[[CCTextureCache sharedTextureCache] removeUnusedTextures];
to force unload of unnesessary textures.

The only way I managed to not slow down the app was with this line:
[_background setTexture: [[CCTexture2D alloc] initWithImage:...]]
When I try to use the shared Texture Cache, even asynchronously, the app slows down. :-O

Related

set UIImage:nil in scrollview doesn't release memory with ARC if already referenced outside of scrollview

Here's what my app does.
It requests about 100 images from the server when it starts up. It stores
those images in model objects, we'll call them modelObject.storedImage
The modelObject also has a viewObject, which is a UIImageView subclass
The app has a 2D scrollview that sets all 100 viewObjects as subviews, with up to 16 of those shown in the scrollview content area at any give time
When you scroll to a given area, it finds the 16 images from the appropriate
modelObject.storedImage and does
modelObject.viewObject.image = modelObjects.storedImage;
When a viewObject scrolls out of the content area, it is purged from the view
[modelObject.viewObject setImage:nil];
[modelObject.viewObject removeFromSuperview];
The problem is, despite the purging, memory keeps growing as you scroll to new areas of the scrollview content area. Memory from putting the former modelObject.viewObject.images on the scrollview is not released. So if you scroll to enough new areas, memory use gets out of control.
To be clear, when the app first loads the 100 images into the modelObject.storedImages, memory does not get out of control. Furthermore, if I comment this out
//modelObject.viewObject.image = modelObjects.storedImage;
then memory also stays fine (but of course the scrollviews subviews don't show any image).
I did see this question "Setting uiimage to nil doesn't release memory with ARC" but I believe it's different because in my app I can't afford to keep requesting images over and over from the server. They must sit at modelObject.storedImage once they've been requested, always ready to be displayed when their corresponding viewObject is scrolled to.
Surely there must be a way to tell ARC to release the memory for these nil images? Thanks in advance for your help!
If images are created by imageNamed:, then no way to manually to release memory, you can optimize it by imageWithContentsOfFile, thanks to you are using images for server, I think you used imageWithContentsOfFile. :)
You can try use #autoreleasepool {} at scrolling method to release memory in real time.
You can try release viewObject instead of removeFromSuperview, maybe what you should store is viewFrame rather than viewObject.
Hope offer you some help.
So I went around this problem and did this as a solution. I'd be curious to see if someone has a more robust idea. I made a copy of the image and set the copy on the scrollview. Now when I purge that copy, its memory is actually released (thank you!!), while the original image still sits comfortably in the model. Note, it did not work to simply copy the image's CGImage and make a copy off that, in which case memory did not release. I had to make a new copy using a UIGraphicsImageContext.
Code to set the image on Scrollview:
UIImage *imageToCopy = modelObject.imageStore;
CGSize size = imageToCopy.size;
UIGraphicsBeginImageContext(size);
[imageToCopy drawInRect:CGRectMake(0, 0, imageToCopy.size.width, imageToCopy.size.height)];
UIImage *copiedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[modelObject.viewObject setImage:copiedImage];
Code to purge when it's off the scrollview's content area:
[modelObject.viewObject setImage:nil];
[modelObject.viewObject removeFromSuperview];
The only problem was that drawing those image contexts used a lot of memory, so I added this modification to make the drawing smaller:
...
CGSize size = imageToCopy.size;
size.width *= .2;
size.height *= .2;
UIGraphicsBeginImageContext(size);
[imageToCopy drawInRect:CGRectMake(0, 0, imageToCopy.size.width*.2, imageToCopy.size.height*.2)];
...

Using the sprite frame cache in cocos2d-the right way

I am using the sprite frame cache to upload plists,and sprite-sheets,to use for animation .
i have 2 approaches to that and i am sure that one of them is wrong .
i have to load everything i can to cache at the start because it takes time, so at the start of the scene i load all what i need to the close future .(but now my cache is full! )
cache should stay as empty as i can , so i am loading to cache at the moment i start the animation (it takes some time isn't it ? i think it flicks my game ) , and remove it at the same moment i am done with it . (now cache is not full but i have to load/reload many times.)
do i have to take care to remove the unused sprites from cache every time ?
load to cache is :
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrameByName:#"stopAnim.plist"];
removing unused is :
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
i can see in my iPad that the system is trying to remove unused sprites and have problems.
whats exactly the way to work ?
After lots of research , i came to some conclusions :
First , it do takes time to load the frames in real time (while the animation is starting) to the batchNode , and in cases of big images (iPad) it sometimes freezing the scene .
So , in some cases like that ,you MUST load the images to cache at the init method to avoid that, because you have no other way,and this is why we have cache anyway :
[[CCTextureCache sharedTextureCache] addImage:#"the_heavy_image.png"];
and if this animation happens frequently , you don't want to clear it from the cache .
Other stuff should be cleared from cache if you dont use it
[[CCTextureCache sharedTextureCache] removeUnusedTextures];
will clear from cache only the sprites that was removed from screen already.
its not enough to just clear the frames :
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
In other scenarios where sprites-sheets images are not that big, you can load them to the batchNode at the moment the animation is starts, and remove them at the end .

Problems of memory developing games in cocos2d

I'm developing a game for iPad. When I run the game on ipad and ride scene presence for the ipad automatically closes the application for lack of memory.
With TexturePacker converted images to pvr, but going from scene to scene'm not able to release memory.
[CCTextureCache sharedTextureCache] removeUnusedTextures];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
I am using these lines but not being enough.
Any idea?
Please consider:
Use instruments leak tool - try making snapshots at the same specific moment (for instance when you are ready to run your scene) then check what was allocated between them.
Try printing memory used by your application in specific parts of your program to find out where the memory is leaking.
Try to find out which dealloc's are not called when you remove your scene then go deeper.
Try calling general functions instead of unused ones:
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
[[CCTextureCache sharedTextureCache] removeAllTextures];
After you remove unused textures, use :
[CCAnimationCache purgeSharedAnimationCache];
[[CCSpriteFrameCache sharedSpriteFrameCache] removeUnusedSpriteFrames];
[[CCDirector sharedDirector] purgeCachedData];
CCLOG(#"%#<applicationDidReceiveMemoryWarning> : after purge", self.class);
[[CCTextureCache sharedTextureCache] dumpCachedTextureInfo];
This will list the textures that are still retained in the cache, and the amount of memory they consume. Any texture that is still present when you expect it to be disposed, is probably there because somewhere along the lines (of code ;) ), you retained them 'somehow' (for example, adding them to an array).

page-based with storyboard dont dealloc

I am developing a complete application using the template "page-based storyboard".
But whenever I turn page, I see thru the instruments that the amount of memory allocated only increase and never decrease, so until a crash occurs.
Trying in iPad device crash too.
To simplify and try to find the problem, I created a test app using the same template and opting for ARC, only loading the image of the pages that I use and no change anything in the original apple code, even in this simple application the problem occurs.
I think the problem is because all page are stay allocated like this issue
PageViewController: How to release ViewControllers added to it?
but I m using storyboard, so, where is:
PageView *pView = [[PageView alloc] init];
I have:
MWViewController *dataViewController = [storyboard instantiateViewControllerWithIdentifier:#"MWDataViewController"]
I try to put autorelease but no effect.
The problem that I was having is that I was using background image in all pages and the imageNamed: method caches images which makes my memory footprint grow. I used the UIImage initWithContentsOfFile: method and my foot print stayed mostly flat.

How to efficiently show many Images? (iPhone programming)

In my application I needed something like a particle system so I did the following:
While the application initializes I load a UIImage
laserImage = [UIImage imageNamed:#"laser.png"];
UIImage *laserImage is declared in the Interface of my Controller. Now every time I need a new particle this code makes one:
// add new Laserimage
UIImageView *newLaser = [[UIImageView alloc] initWithImage:laserImage];
[newLaser setTag:[model.lasers count]-9];
[newLaser setBounds:CGRectMake(0, 0, 17, 1)];
[newLaser setOpaque:YES];
[self.view addSubview:newLaser];
[newLaser release];
Please notice that the images are only 17px * 1px small and model.lasers is a internal array to do all the calculating seperated from graphical output. So in my main drawing loop I set all the UIImageView's positions to the calculated positions in my model.lasers array:
for (int i = 0; i < [model.lasers count]; i++) {
[[self.view viewWithTag:i+10] setCenter:[[model.lasers objectAtIndex:i] pos]];
}
I incremented the tags by 10 because the default is 0 and I don't want to move all the views with the default tag.
So the animation looks fine with about 10 - 20 images but really gets slow when working with about 60 images. So my question is: Is there any way to optimize this without starting over in OpenGl ES?
As jeff7 and FenderMostro said, you're using the high-level API (UIKit), and you'd have better performance using the lower APIs, either CoreAnimation or OpenGL. (cocos2d is built on top of OpenGL)
Your best option would be to use CALayers instead of UIImageViews, get a CGImageRef from your UIImage and set it as the contents for these layers.
Also, you might want to keep a pool of CALayers and reuse them by hiding/showing as necessary. 60 CALayers of 17*1 pixels is not much, I've been doing it with hundreds of them without needing extra optimization.
This way, the images will already be decompressed and available in video memory. When using UIKit, everything goes through the CPU, not to mention the creation of UIViews which are pretty heavy objects.
Seems like you're trying to code a game by using the UIKit API, which is not really very suitable for this kind of purpose. You are expending the device's resources whenever you allocate a UIView, which incurs slowdowns because object creation is costly. You might be able to obtain the performance you want by dropping to CoreAnimation though, which is really good at drawing hundreds of images in a limited time frame, although it would still be much better if you used OpenGL or an engine like Cocos2d.
The UIImageView is made to display single OR multiple images. So, instead of creating every time a UIImageView, you should consider creating a new image and add it to the UIImageView instead.
See here.
I'd recommend starting over using OpenGL ES, there is an excellent framework called cocos2d for iPhone that can make this type of programming very easy and fast. From a quick look at your code, you're lasers can be remodeled as CCSprite which is an easy way to move images around a scene among many other things.