Preloading Sprite Sheets with cocos2d - ios

I have a game which has many unique units, and each unit has its own sprite sheet (because each unit has several animations), meaning there may be upwards of a dozen sprite sheets used by the game on any given scene. When the scene with the units is created, the initialization of all these assets takes a lot of time (a couple seconds). Specifically, the UI is locked while this code is run:
NSString *unitSheetName = [self getUnitSheetName];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:unitSheetName];
CCSprite *frame = [CCSprite spriteWithSpriteFrameName:kDefaultFrameName];
// etc...
However, if I then pop the scene, and then create a new version of the same scene (and push it), the loading takes a fraction of the time (the UI does not lock). Thus, it seems the UI locking is due to the initial caching of the sprite sheets...
My hope was that I'd be able to avoid this lag by calling the CCSpriteFrameCache's addSpriteFramesWithFile method while the game is starting up, but this does not seem to work. Despite doing this, the initial load still takes a long time.
Is there any good, effective way to preload the assets? I don't mind adding a second or two to my startup loading screen, if only I can be sure that the UI will later not be locked when the scene is pushed...

Per #LearnCocos2D's reply, above, here's how to properly and fully pre-load a sprite sheet, (fpSheet is the file path of the sheet):
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:fpSheet];
NSDictionary *spriteSheet = [NSDictionary dictionaryWithContentsOfFile:fpSheet];
NSString *fnTexture = spriteSheet[#"metadata"][#"textureFileName"];
[[CCTextureCache sharedTextureCache] addImage:fnTexture];

Related

Most performant way to load textures from a Spritekit texture atlas

I cannot figure out how to load my textures in a brief few seconds. It is taking a monster 20+ seconds.
Initially I had my animations in separate texture atlases. They are named something like sprite_002#2x~iphone.png. When they were separate I used code similar to that in the Apple documentation:
SKTextureAtlas *atlas = [SKTextureAtlas atlasNamed:#"monster"];
SKTexture *f1 = [atlas textureNamed:#"monster-walk1.png"];
SKTexture *f2 = [atlas textureNamed:#"monster-walk2.png"];
SKTexture *f3 = [atlas textureNamed:#"monster-walk3.png"];
SKTexture *f4 = [atlas textureNamed:#"monster-walk4.png"];
NSArray *monsterWalkTextures = #[f1,f2,f3,f4];
My code was:
let names = myAtlas.textureNames as [String];
for name in names {
myTextureArray.append(myAtlas.textureNamed(name as String));
}
For 6 animations in 6 different texture atlases w/ 250 frames each, it took about 6 seconds to load.
I was able to eliminate 60-70% of those frames and cram everything into a single texture atlas, but now I cannot get it to load in a reasonable time frame.
I have been trying code along these lines:
for i in 1...124 {
let name = String(format: "sprite_%03d", i);
let texture = imagesAtlas.textureNamed(name);
spriteTextures.append(texture);
}
Most games iPhone games don't have a 20 second load time, so I know I am doing something wrong here.
Your load time will be dependent upon the size of your atlases. So if you happen to have a large number of them and they are large, it will take some time.
I've seen variations of this type of question here before.
My suggestion would be to actually change your approach to the problem. This is breaking apart the workload and doing it in the background over time. This is why many games have loading screens.
Depending on your game, you may be able to be extra sneaky and load textures (or any asset for that matter) in the background while the game is playing. More often than not, you don't need to load all your textures at the start of a game.
When I make my games, one of the first things I do is create a background loader, if I don't already have one. I then build the game around the premise of async loading of assets.
20 seconds is pretty long. But based on your description, it sounds like it would take a non trivial amount of time.

Weird nosense CCSpriteBatchNode Exception

There is something weird going on in my cocos2d game I can't figure out the problem.
I have a very simple files structure for my game, 1 intro layer, 1 main menu layer, 1 game layer.
While I was developing I skiped the menu layer in order for the game jump from intro layer to save time.
Now I finished the game, I added back the main menu and when game layer is called from it it crashes!!??
it never crashes if I skip the main menu layer.
Edit:
Important, If I have remove the main menu layer it works just fine... main menu doesn't add any texture atlas, there is only one I am using entire the whole game.
this is the message error:
"CCSprite: Batched sprites should use the same texture as the batchnode"
from this gamelayer code:
_myitens = [CCSpriteBatchNode batchNodeWithFile:#"mytextureatlas.png";
The funny thing is Main menu Layer only feature is to have a button(image from file and not texture atlas) that calls the game layer....
it is very confusing I can't see how to approach this issue
any tips are valid thanks
One of the more common causes, and in particular if the problem depends on which scene you load first, occurs when you have the same image in two atlases.
For instance, assuming you have mytextureatlas.plist and notmytextureatlas.plist and both contain an image with the same name, say "player.png". If you first load mytextureatlas all seems fine. However when you load notmytextureatlas first, and later try to add a player.png sprite to the batch node using mytextureatlas, cocos2d will actually use the player.png from notmytextureatlas, provoking this error.
Double-check that all image names across all texture atlases are unique.
How are you adding sprites to _myitems? You are adding children which have been created with a different texture, that's the meaning of the error and that's what happening.
The exception is not non-sense, it means a precise condition which somewhere you fail to obey: a batchnode must have children initialized with the same texture of the batchnode itself (and it makes sense, since it's used to batch GL calls).
For example:
_myitems = [CCSpriteBatchNode batchNodeWithFile:#"mytextureatlas.png"];
[_myitems addChild:[CCSprite spriteWithTexture:#"othertexture.png"]];
Will generate the error. You should try with:
[_myitems addChild:[CCSprite spriteWithTexture:_myitems.textureAtlas.texture]];

Checking/removing SKScene sprite children easily/efficiently?

Working on an iOS game using SpriteKit. My background is made up of map tiles (essentially an infinite map, procedurally generated).
Our system is designed to manage "chunks" of the map, and we only load chunks near the player. Since SpriteKit requires we add SKSpriteNodes, we no longer have clean control over "unloading" sprites for chunks/tiles that are no longer near the player.
I realize SpriteKit won't actually render things off-screen, but it's going to kill performance if we can't remove sprites no longer needed, or check if a chunk/tile is already added.
Since SKNodes doesn't respond to isEqual:, I only see two ways to do this:
Give each sprite a name with their chunk/tile coordinate, and check this name each update
Maintain a separate array of loaded tiles and check that instead
Is there any easier way of checking/removing if a sprite has been added already? Maybe a partial string match on sprite name?
I'm not sure that using SpriteKit is the best solution (Xcode simulator seems to drag at 30fps, have yet to test on a real device). We originally built this game in Java and we're rendering our own textures - hence only what was loaded and could be fed into opengl manually.
-(void) renderToScene:(SKScene *)scene {
for( Chunk *chunk in loadedChunks ){
for( Tile *tile in [chunk getTiles] ){
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithTexture:tileTexture];
sprite.name = #"Tile";
sprite.position = CGPointMake(realX,realY);
[scene addChild:sprite];
}
}
}
What will in fact kill your framerate is frequently creating and removing nodes - you should have a pool of sprites that you re-use rather than recreate.
Just update the sprite's texture, position and other attributes when reusing one of those you no longer need. A common use case is to have enough tiles to span the entire screen plus one row and one column, so that when an entire row or column has moved outside the screen you can reposition it at the other side with new textures according to the map data.
If i'm understanding what you're asking correctly. You want to properly remove a node/sprite from the scene if its no longer within view.
You should just be able to call the [self removeFromParent] method to remove in whenever its outside the bounds of the screen. Remember you can call this method on any object as long as its a child.
For instance if i had character,
SKSpriteNode *character;
SKSpriteNode *sword;
//all the rest of the properties are up to you.
if my character picked up a sword in the game and after a certain time period the sword is no longer accessible. I would do:
[character addChild:sword];
which would add it to the character.
[sword removeFromParent];
when i no longer need the sword to be apart of its parent.

Clearing elements from a CCBI file from CCTextureCache and CCSpriteFrameCache

Currently i'm making a game using cocos2d and i've come up on a situation that i'm having trouble dealing with. The game has a loading screen that uses a CCB file and is read in using CCBReader and this causes it to be added to the caches. After the user exits the loading screen and enters the game i would like to clear the textures that the loading screen uses from the cache. I know you can use something like
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrameByName:item];
[[CCTextureCache sharedTextureCache] removeTextureForKey:item];
but this doesn't clear it from the texture when loaded from a CCB file. Any body have this situation before or knows how to deal with it?
Instantiate and use a CCSpriteFrameCache and CCTextureCache for your own assets. Then clear the shared caches completely:
[[CCSpriteFrameCache sharedSpriteFrameCache] removeSpriteFrames];
[[CCTextureCache sharedTextureCache] removeAllTextures];

How to load animation in background in cocos2d?

I am currently developing one cocos2d game for iPad, in that game lot of animations is there. I have previously used zwoptex for creating spritesheet and adding animations in my project.
In my game 10 Levels is there, and after each level completed one animation will play, the animation is different for each level and animation image size is same as device screen size. so i am not create spritesheet file, instead of i am loading image directly. My problem is while animation it takes too much time for animation playing.
How to i fix it? please any one guide me. is it possible to load full screensize image(1024x768) in plist, because total 10 levels each level has 20 frames for animation, so 10X20 = 200 images need to load spritesheet.
I am using this code for animation
CCAnimation *animation = [CCAnimation animation];
for(int i=1;i<=20;i++)
{
[animation addFrameWithFile:[NSString stringWithFormat:#"Level%dAni%d.png",level,i];
}
animation.delayPerUnit = 0.3f;
animation.restoreOriginalFrame = YES;
id action = [CCAnimate actionWithAnimation:animation];
My Question is it possible to load full screen animations with spritesheet? and animation loading time is differ and it takes too much time how to fix it?
Please help me..
Do the math and figure out the amount of memory you would need for 200 pics at 1024x768 pixels. Way too much memory for any iSomething device.
If you have a performance problem (ie are you running this on a device?), then there are two things you can do to improve image load speed:
Convert your images to .pvr.gz (i recommend TexturePacker for this). They load significantly faster than .png.
Use an RGBA4444 pixel format (again you can do this with TexturePacker), and set the texture format in cocos just prior to loading the images. Images will be smaller, take much less memory.
In your code, where you do the anim
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA4444];
// load your images and do your anim
...
// at the completion of the anim
[CCTexture2D setDefaultAlphaPixelFormat:kCCTexture2DPixelFormat_RGBA8888];
By Using Cocos2D you can animate sprite sheet images easily.
Try following examples using Cocos2D
http://www.smashious.com/cocos2d-sprite-sheet-tutorial-animating-a-flying-bird/309/ http://www.raywenderlich.com/1271/how-to-use-animations-and-sprite-sheets-in-cocos2d
Try following examples without using Cocos2D
http://www.dalmob.org/2010/12/07/ios-sprite-animations-with-notifications/
http://developer.glitch.com/blog/2011/11/10/avatar-animations-in-ios/
Use NSThread and load your animation in this thread.
NSThread *thread = [[[NSThread alloc] initWithTarget:self selector:#selector(preloadFireWorkAnimation) object:nil] autorelease];
[thread start];
-(void)preloadFireWorkAnimation
{
// load your animation here;
}

Resources