How to load animation in background in cocos2d? - ios

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;
}

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.

How to use pixel art in an app?

I have an iOS app that uses sprite kit, and I am ready to add my artwork. The artwork is pixel-art and is inherently very small. I am trying to find the best way to display this in way where:
All of the art is the same size, meaning that one image pixel takes up exactly the amount of real world pixels as in all the other images.
There is no blurring in an attempt to make the textures look smoother, which often happens when scaling images up.
I have tried solving the second one like so:
self = [super init];
if(self){
self.size=size;
self.texture = [SKTexture textureWithImageNamed:#"ForestTree1.png"];
self.texture.filteringMode = SKTextureFilteringNearest;
[self.texture size];
}
return self;
The above code is in the initialization of the SKSpriteNode which will have the texture.
This is my original image (scaled up for easy reference):
The problem is that my result always looks like this:
(The bottom of the trunk being offset is not part of this question.) I am not using any motion blur or anything like it. I'm not sure why it isn't displaying correctly.
Edit 1:
I failed to mention above that the trees were constantly animating when the screenshots were taken. When they are still they look like this:
The image above is of two trees overlapping with one flipped caused because of a bug to be fixed later. My question is now how can I prevent the image from blurring while animation is occurring?
Edit 2:
I am adding multiple instances of the tree, each one loading the same texture. I know it as nothing to do with the animation because I changed the code to add just one tree and animate it, and it was pixelated perfectly.
You need to use "nearest" filtering:
self.texture.filteringMode = SKTextureFilteringNearest;
The pixels in your image must correspond with pixels on the screen perfectly.
If your image is 100x100, and you display it over a whole screen that is 105x105, it will do interpolation to figure out how to do it.
If you display it at a scaled resolution of some multiple of 2 (which should work properly), I think you still have to tell the renderer not to interpolate pixels when it does the scaling.
I've solved the problem...but its really a hack. I have a SKScene which is the parent node to all of the "trees" (SKSpriteNodes). This scene will be adding multiple trees to itself. At first I thought that this was some sort of problem because if I only added one tree, it would display the image correctly. The answer to this question led me to believe that I would need to programmatically create a SKTextureAtlas singleton in the (the texture is in a SKTextureAtlas) and pass it to the tree class to get the texture from on an init method. I made a property in the SKScene to hold the texture atlas so that I could pass it to the tree class every time I made a new one. I tried loading the texture from texture atlas (in the tree class) using the textureNamed: method. This still did not work. I switched back to loading the texture with SKTexture's textureWithImageNamed: method and it worked. Further more I changed to code back so that the tree subclass would not be sent the SKTextureAtlas singleton at all and it still worked.
In the SKScene I get the texture atlas using:
[SKTextureAtlas atlasNamed:#"Textures"]; //Textures is the atlas name.
and set the return value to be the SKTextureAtlas property described above. I thought that maybe the atlas just had to initialized at some point in the code, so I tried this:
SKTextureAtlas *myAtlas = [SKTextureAtlas atlasNamed:#"Textures"];
and the following alone on one line:
[SKTextureAtlas atlasNamed:#"Textures"]
but neither worked. Apparently I need to have a property in my tree's parent class which is the SKTextureAtlas which holds the texture which the tree uses without any reference to a SKTextureAtlas whatsoever... Is this a glitch or something? It's working now but it feels like a hack.
[self setScaleMode:SKSceneScaleModeAspectFill];
SKTexture* texture = [SKTexture textureWithImageNamed:#"image"];
[texture setFilteringMode:SKTextureFilteringNearest];
SKSpriteNode* imageNode = [SKSpriteNode spriteNodeWithTexture:texture];
[self addChild:imageNode];
Works perfectly for me. There's no blur with animation

Preloading Sprite Sheets with cocos2d

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];

UIImageView displays MUCH faster than either CG or CALayer. Anyone know why?

I've written an app to test image performance on iOS. I've tried 3 different views, all displaying the same large PNG. The first is a view that draws using CGContextDrawImage(). The second sets self.layer.content. The third is a plain UIImageView.
The image used is created using -[UIImage initWithContentsOfData:] and cached in the viewController. Each test repeatedly allocs a view, adds it to the view hierarchy and then removes it and releases it. Timings are taken from the start of loadView to viewDidAppear and given as fps (effectively, view draws per second).
Here are the results from an iPad 1 running 5.1 using a 912 x 634 unscaled image:
CGContext: 11 fps
CALayer: 10 fps
UIImageView: 430 fps (!)
Am I hallucinating? It seems almost impossible that UIImageView can draw that fast, but I can actually watch the images flicker. I tried swapping between two similar views to defeat possible caching, but the frame rate was even higher.
I had always assumed that UIImageView was just a wrapper for -[CALayer setContent]. However, profiling the UIImageView shows almost no time spent in any drawing method that I can identify.
I'd love to understand what's going on. Any help would be most appreciated.
Here is my idea.
When you modify a UIView hierarchy, by either adding, removing or modifying some view, no actual drawing is performed yet. Instead the view is marked with 'setNeedsDisplay', and is redrawn the next time the runloop is free to draw.
In fact, if your testing code looks something like this (I can only guess):
for (int i=0; i<10000; i++) {
UIImageView* imageView = [[UIImageView alloc] initWithFrame:frame];
[mainView addSubview: imageView];
[imageView setImage: image];
[mainView removeSubview: imageView];
}
than the runloop is blocked until this for loop is done. The view hierarchy is drawn only once, the measured performance is that of allocating and initializing objects.
On the other hand, the CGContextDrawImage() is free to draw right away, I think.
The high FpS with UIImageView is because this does not actually redraws, but only marks the content for redrawing sometimes in the future when UIKit feels like doing it.
As a note:
What is strange though is, that the CGContextmethod is faster than the CALayermethod. I also tried these methods in a project of mine and working with CALayer is the fastest method (except using OpenGL ES of course).
UIImageView uses a different way of rendering images that is much more efficient. You should have a look at this session video from WWDC 2011 that explains how the rendering process works: https://developer.apple.com/videos/wwdc/2011/?id=121

How to keep iOS animation smooth with many subviews

I am trying out different looks of a little game I am writing to play with animation on iOS.
My goal is this to have a grid of tiles which based on gameplay changes the display of each tile to one of a set of images. I'd like each tile (up to 24x24) to flip around when its face changes. As the game progresses, more and more tiles need to be flipped at the same time. In the first implementation where I tried to flip them simultaneously the animation got very jerky.
I changed my approach to not flip them all at once, but just a few at a time, by scheduling the animation for each tile with a slightly increasing delay per tile, so that when say the 10th tile starts animating, the first one is already done. It takes little while longer for the whole process to finish, but also leads to a nice visual ripple-effect.
However, one problem remains: At the beginning of a game move, when the player picks a new color, it takes a few fractions of a second on the device, before the animation starts. This gets worse as the game progresses and more flips need to be scheduled per move, up to the point where the animation seems to hang and then completes almost instantly without any of the frames in between being actually discernible.
This is the code (in my UIView game grid subclass) that triggers the flipping of relevant tiles. (I removed an optimization that skips tiles, because it only matters in the early stages of the game).
float delay = 0.0f;
for (NSUInteger row=0; row<numRows; row++) {
for (NSUInteger col=0; col<numCols; col++) {
delay += 0.03f;
[self updateFlippingImageAtRow:row col:col delay:delay animated:YES];
}
}
The game grid view has an NSArray of tile subviews which are addressed using the row and col variables in the loop above.
updateFlippingImageAtRow:col:delay:animated is a method in my FlippingImageView (also a subclass of UIView) boils down to this (game logic omitted):
-(void)animateToShow:(UIImage*)image
duration:(NSTimeInterval)time
delay:(float)delay
completion:(void (^)(BOOL finished))completion
{
[UIView animateWithDuration:time
delay:delay
options:UIViewAnimationOptionTransitionFlipFromLeft
animations:^{
[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft
forView:self
cache:YES];
self.frontImage = image;
}
completion:completion
];
}
Works fine, however, I conclude from the Instruments measuring which tells me that my time is spent in the animation block, that as the game goes on and the number of tiles to flip goes up, that the number of animations that get scheduled at the very beginning of the operation is a problem, and that the system then tries to catch up by dropping frames.
Any suggestions how to improve the performance of this? The exact timing of the animation is not really important.
You can think about doing this with CoreAnimation and CALayers instead of UIViews. It is incredebly powerful and optimized framework.
It's not an easy thing, you'll have to recode at least some of your classes (view hierarchy and hit tests are the first things that come to my mind), but it's worth a try and it's rather painless process, because CALayer is very similar to UIView.
Next step is OpenGL. It definitely can operate several hundreds of objects in realtime, but it requires much more work to do.
You might want to try using CoreAnimation instead. One way to do the flip animation would be:
Create a CABasicAnimation that animates the first half of the flip (rotation along the y axis).
In the delegate method animationDidStop:finished: you set the new image and then create a new animation that animates the second half.
You simply apply the animation to the layer property of your view.
Note that you can use setValue:forKey: to "annotate" an animation (remember what object the animation is about). When you add an animation to a layer it gets copied, not retained, so you can't identify it by simply comparing pointer values.

Resources