How to use sprite sheets effectively? - memory-management

What I want to do:
I want to use sprite sheets to load all my enemies in the game. They would have to be removed once they are either destroyed by the good guy or when they go off screen. I have 6-7 enemies some of which are animations. I will be reusing them multiple times. I wan to load and unload them effectively from the memory.
What I am doing:
I first load the spritesheets:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"Obstacles.plist"];
CCSpriteBatchNode *obstaclesspriteSheet = [CCSpriteBatchNode batchNodeWithFile:#"Obstacles.pvr.ccz"];
[self addChild:obstaclesspriteSheet];
I have a class called BadBoys which handles the bad guys. Every time I want to create a bad guy I create an instance of the class. Inside the class I create the sprite and add it to the layer.
baddies[x] = [[BadBoys alloc] init];
//INSIDE THE CLASS
baddie = [CCSprite spriteWithSpriteFrameName:#"cannonball-hd.png"];
I remove the sprite from the layer when it gets destroyed and then release the instance of the class.
[self removeChild:[baddies[x] getBaddie] cleanup:YES]; //getBaddie returns the sprite
[baddies[x] release];
I know this is a good way to do it. What I want to know is if this is the most efficient way of doing this? I thought of another way of loading the image:
Load the image asynchronously to the CCTextureCache.
Then create a sprite using the texture from the cache.
Add it to a NSMutableArray which will hold all enemies that are alive
Then when I dont need it anymore I can destroy it the following way:
CCTexture2D * texture = spriteName.texture;
[spriteName.parent removeChild:spriteName cleanup:YES];
[[CCTextureCache sharedTextureCache] removeTexture:texture];
[backgroundSprites removeObject:spriteName];
Is this a better method? Please share your views and suggestions. Thanks.

What you're doing is good, as you said.
Your alternative is a terrible idea. I'll tell you why: you will want to avoid loading textures into memory and removing them from memory frequently during gameplay. Loading a texture from flash memory is slow. Removing the texture from memory also takes a little time, and is rather pointless if you have enough free memory available anyway.
Furthermore if you're using a spritesheet and you're removing one obstacle but try to remove the obstacle's texture (which is Obstacles.pvr.ccz) from memory as well, then the texture cache won't remove the texture from memory anyway. Because it's still being used by the spritesheet.
Lastly: premature optimization is the root of all evil.

Related

Choppy/Slow Animation on first load cocos2d

Write now, I'm creating an interactive ebook using cocos2d. When the program runs the cover page animation is choppy only on initial load. This animation is on the cover page and it is huge. We're talking 13 texture atlases, 26 images.
I know the problem stems from preloading/loading images, but I already loaded all the images before the animation occurs.
-(id) init {
if( (self=[super init]) ) {
isTouchEnabled_=YES;
//Pg0 Animations
[[CCSpriteFrameCache sharedSpriteFrameCache]addSpriteFramesWithFile:#"CB0A0.plist"];
[[CCSpriteFrameCache sharedSpriteFrameCache]addSpriteFramesWithFile:#"CB0A1.plist"];
...etc.}}
The overall set up of the book is as follows. I build a scene, a menu layer, and then the menu layer pushes and pops different layers/ different pages.
When the cover page is loaded (Pg0), if the user touches a picture it runs through the animation. Then when the user, touches the picture again, a second animation runs.
All the images of the book are loaded in the menu layer (so before any page pushes or pops). I've tried loading these images on the scene, or the individual pages to speed up the process, but that doesn't affect time.
I am running my animation through an animation helper, which loops through the images for me.
Any help would be wonderful, and I am would love to share more code or info about the project if it needed.
Have the same problem. When use cocos2d v0.9 animation was ok, no lag. Lag appears when migrating to cocos2d v2.0.
When you add sprites to cache (addSpriteFramesWithFile:(NSString*)plist) in cocos2d v0.9 texture2d is also created (CCSpriteFrameCache.m:238), in cocos2d v2.0 there is no textture2d creation, texture is created at first use of CCSpriteFrame. I fix it by adding
[[CCTextureCache sharedTextureCache] addImage:textureFileName];
in CCSpriteFrameCache.m:207 (v2.0)
I was just experiencing this problem in cocos2D v3.1 iphone. I had spritesheets that were cached but still were stuttering on loading. I found somewhere on another post that you need to load the texture itself as well to get rid of the initial lag. This is what I did in my caching sprites method:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"spritesheet.plist"];
CCTexture *temporaryLoadTexture = [CCTexture textureWithFile:#"spritesheet.png"];
Just by loading a dummy CCTexture file, that got rid of all first-time animation and sprite lag. Hope this helps!
Check if your AppDelegate implements the memory warning method. Check what that method does.
By default, cocos2d implements that method and purges all caches. Now when that gets triggered while preloading textures, what happens is that cocos2d removes the preloaded textures because a memory warning was received yet those preloaded textures aren't in use yet. Cocos2d considers them to be "unused" and removes them from memory. Which means next time you use that texture, it needs to be loaded from disk again.
I wrote a blog post where I describe this issue, and ways to solve it. Most importantly: get TexturePacker and use only .pvr.ccz for texture atlases. Try to reduce texture color depth to 16 bit where possible. And be sure to dump texture memory usage to learn how much memory you're actually using, and whether that amount of memory is within reasonable limits. Including older devices with fewer memory that you may be supporting.

how to draw many CCSprite without slow performance?

I'm making such a arrow shooting game. Everything is good. but I realized if I draw line tracking my arrow, It will be great. so I put some code on my game in my Scheduler that is supposed to draw circle where arrow is going. But I had to draw so many circle, so game frame is not good when I shoot multi arrow.
Is there other better way? I use CCSpriteBatchNode, plist, CCSpriteFrameCache already. I did all I can do. I need help Thanks so much
this is my code
...............
[self schedule:#selector(CollisionDetection:)];
}
- (void)CollisionDetection:(ccTime)dt
{
for (CCSprite *arrow in arrows->arrowsArray)
{
CCSprite *track = [CCSprite spriteWithSpriteFrameName:#"WhiteCircle.png"];
[track setPosition:arrow.position];
[arrows->rootLayer->arrowsSheet addChild:track];
id delete = [CCFadeOut actionWithDuration:1.0];
id deleteAction= [CCSequence actions:delete ,[CCCallFuncN actionWithTarget:self selector:#selector(spriteActionFinished:)], nil];
[track runAction:deleteAction];
.......
The allocation of objects is a large overhead. If your game runs to slow you should consider creating a pool of arrows at the beginning of the game and only trigger the action on it as soon as you need it. If it is not visible anymore just set it to inactive and reuse it the next time you need an arrow.
Sounds like your problem isn't the arrows, but the circles, which I imagine are Cocos objects too. You'd be better off learning how to draw the circle texture directly to the screen using opengl commands instead of objects. That'll help a lot.
When have a time critical animation, i stay clear from CCCallFunc(N), as it can stall the scheduler for a noticeable amount of time. As I read your code, you have a CallFunc at every scheduled interval ... hmmmm. Have you tried running this without the scheduler, ie package and start all your animations at once, with a single CallFunc at the end ? Instead of changing the position as part of the scheduled interval, use a CCMoveTo that you will run at the same time as your tracking animation.

Performance navigating through several full screen images on iPad

I need to let user navigate back and forward through different images (from 10 to 20) with a tap.
All the images are 1024x768 JPG's so I don't need to resize or transform them.
There are no animations between them (I'll switch them with a removeFromSuperView and addSubView).
All I want is avoid loading time or unresponding touch, so I actually was thinking about these possible solutions:
Load each image singularly on tap;
Load 3 images: previous, actual and next one;
Load an array or a uiviewimage with all the images and iterate through it;
I will avoid imageNamed and I'll use imageWithContentOfFile or imageWithData.
Which solution do you think is the best one?
Could solutions 1. and 3. bring some performance issue?
Method 1 will be good : iOS devices can load full screen images really fast. Especially if your images don't have alpha. It will depends on images but it takes around 0.05 seconds for a classic png. This means that users will not notice the waiting time if you have to change after a tap especially if you had a fade transition between images.
Things can get harder if user can scroll through images. In this case, I would advise to use UITableView. They behave like UISCrollView and they load/unload pages fastly and smoothly.
To get an horizontal table view, you can use this code which works perfectly : https://github.com/alekseyn/EasyTableView
If your upper limit for number of images is 20, just preload an array of UIImages, and set the UIImageView.image property on response to touch - don't worry about swapping views, reusing a single UIImageView will be fine.
I wouldn't worry about performance unless the upper limit rises much higher - if it does, a dynamic cache like option 2 would be a better choice, but more programming.
If you are concerned about performance in an iPad application destined for the app store, always remember to test on a first generation iPad, since there was a major performance jump after the original.
I have actually done this before. With large images, you are going to have to be careful with memory. I originally loaded all of my images into an NSArray but I experienced memory warning and crashes.
My implementation uses a UIScrollView with paging. I have to arrays, one contains all of the image names and the other is mutable and contains only a few UIImageViews. I record the current 'page' that the scroll view is on, and when I land on an image I ensure that the mutable array contains that image and two images on either side of it (and remove any other images from the array).
The problem with this implementation is that you keep having to read images from disk which will be slow on the main thread. Sooo when I initially create the UIImageViews I add a UIActivityIndicator to them. Then, I pass my array of UIImageViews to a method in the background that actually loads the UIImage and then makes the respective UIImageView set the image on the main thread like so:
// called before you try to load an image in a background thread
[imageView addObserver:self forKeyPath:#"image" options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld context:nil];
// called in the background thread after you load the image from disk
[imageView performSelectorOnMainThread:#selector(setImage:) withObject:fullImage waitUntilDone:NO];`
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
UIImageView *imageView = (UIImageView *)object;
[[imageView viewWithTag:1] removeFromSuperview]; // this is the activity indicator
[imageView removeObserver:self forKeyPath:#"image"];
}

Organizing files in Cocos2d

I have just started learning Cocos2d using the great tutorials
Ray Wenderlich has produced. However I'm getting to a point where my HelloWorldScene main file is getting a little large.
I would like to be able to sort sprite generation methods into one file, misc. methods into another and so on, leaving me with the core level files containing the different scenes and init methods.
Is it possible to copy methods over to a new .m file and bring them into the HelloWorldScene.m when I need them?
How do you organize your game files?
I highly recommend layering your game. It keeps it organised and neater, reducing the amount of code in any one file.
Within cocos2d, there are 3 classes you should take note of to achieve this:
CCDirector, CCScene, CCLayer.
What you can do is, create multiple layers within a single scene (GameScene).Eg:
Have a HUD, Game Interface, Background and Foreground.
These can all be split into their separate CCLayers and added together within the CCScene.
So now you have split HelloWorld.m into:
GameScene.m
HudLayer.m
GameInterfaceLayer.m
Background.m
Forground.m
After importing all the h files into your GameScene.h, You can then add each Layer to the scene as Follows:
#implementation GameScene
+(CCScene){
HudLayer *hud = [HudLayer node];
[self addChild: hud z:3];
GameInterfaceLayer *game = [GameInterfaceLayer node];
[self addChild: game z:1];
...} etc
#end
Once you do this, if you dont change your AppDelegate, your CCDirector will give you an error. CCDirector basically dictates which Scene is loaded.
Change this to point to your GameScene.
You can visualise: the Director of the Movie, Filming a Scene, filled with many Layers.

Efficiently spawning sprites in Cocos2D

Just a quick question here; I have a background image in my game. Every time I spawn an sprite, I use the [self addChild:#""]; statement. Would it be more efficient to to call [backgroundImage addChild:#""]; instead? I have tested both ways and they both work, but I cannot tell which is more efficient, or if it even makes a difference.
Thanks,
Tate
If your object is CCLayer, calling [self addChild:img], the image will be added to the CCLayer.
I believe your background image is a CCSprite. [background addChild:img], will add the sprite to the background. If you adjust your background, like moving it up, all the sprites attached to background(child of background) would be moving together with the background image.
When you called [self removeChild:background], all the child attached to the background would be removed as well..
It depends on what are the requirements of the game.. =)

Resources