Preload Animations in Cocos2D iOS - ios

Im facing a problem with a game Im developing. Ive got several animations and my problem is , they work properly but the first time they are loaded, there is a delay, just for the first time. I dont know how to solve it up. Ive read all the documentation about CCAnimationCache, Ive read the forums and Ive had no success. Im still having the same problem, the animations work but the first time one of them is loaded, there is a delay. Can you please help me with that? Ive also read this site : http://www.johnwordsworth.com/2011/07/loading-cocos2d-sprite-frame-animations-from-plist-files/ and they speak about preloading but my result is the same.
I cache the animations at the beginning and it seems they are in the cache but the result is not the desired all the time and Ive got this delay everytime I load one of the animations for the first time.
Ive coded a function to preload all the animations following the specs and using CCAnimationCache and it doesnt work or maybe Im wrong. Here is some code Ive made :
for(int element = 0; element < 3; element++){
for(int pos = 0; pos < 3; pos++){
CCAnimation *auxAnimation = nil;
animation = getAnimationInformationWithElement(element, pos);
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:[NSString stringWithFormat:#"%#.plist",animation.animationFileHeader]];
for(int i = 0; i <= animation.numberOfFrames; i++) {
if(i<=9)auxFileName = [animation.animationPNGFileName stringByAppendingString:#"0000"];
else auxFileName = [animation.animationPNGFileName stringByAppendingString:#"000"];
[elementsAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"%#%d.png",auxFileName,i]]];
}
auxAnimation = [CCAnimation elementsAnimFrames];
[animationsToBeSaved elementsAnimFrames];
elementsAnimFrames = [[[NSMutableArray alloc] init] autorelease];
[[CCAnimationCache sharedAnimationCache] addAnimation:auxAnimation name:[NSString stringWithFormat:#"animation_%d_%d",element,pos]];
}
}
Any ideas?

Assuming your animations are .png Spritesheets, I recommend you preload your images asynchronously into cache.
/** Asynchronously, load a texture2d from a file.
* If the file image was previously loaded, it will use it.
* Otherwise it will load a texture in a new thread, and when the image is loaded, the block will be called.
* The callback will be called in the cocos2d thread, so it is safe to create any cocos2d object from the callback.
* Supported image extensions: .png, .bmp, .tiff, .jpeg, .pvr, .gif
* #since v2.0
*/
-(void) addImageAsync:(NSString*) filename withBlock:(void(^)(CCTexture2D *tex))block;
For example, use it as follows:
[[CCTextureCache sharedTextureCache] addImageAsync:#"spritesheetName.png" withBlock:^(CCTexture2D *texture){
#synchronized(self){
if (texture)
//increment preloaded assets count or whatever..
}
}];

Related

Changing UICollectionView cell image at regular interval

I am developing an IOS 10.x application using UICollectionView and would like to change the image of specific cells, at regular interval.
The code below shows the current implementation. Even though it should change the cell background image every half of second, it changes the images immediately disregarding the NSThread SleepAt interval of 0.5 seconds.
I suspect something about the main thread handling or the ReloadItem method but hasn't reached a clear conclusion. Any insight is very welcome! Thank you.
NSNumber* originalCardSelected;
int position;
for (int i = 0; i < [opponentOriginalCardArray count]; i++) {
originalCardSelected = [opponentOriginalCardArray objectAtIndex:i];
position = [self convertLevelDataPositionToCellViewPosition:[originalCardSelected intValue]];
NSMutableArray *tmpBackgroundAssets = [self getNewAssetBackgroundBasedOnBackgroundType:playbackBackground Index:position];
self.backgroundAssets = tmpBackgroundAssets;
dispatch_async(dispatch_get_main_queue(), ^{
[collectionViewRoot reloadItemsAtIndexPaths:[collectionViewRoot indexPathsForVisibleItems]];
});
[NSThread sleepForTimeInterval:0.5];
}
You should use performBatchUpdates(_:completion:) methods and add your for loop inside it.
Remember to not keep strong references inside the block to avoid retain cycles.
EDIT:
and in the completion block, you can check if finished and add your NSThread methods

ios Loading texture from atlas not working

I have an atlas with a bunch of tiles and i am trying to load them into memory using SKTexture and SKTextureAtlas but it is not working. I use the following code to load them:
NSString *atlasName = [NSString stringWithFormat:#"Tiles"];
SKTextureAtlas *tileAtlas = [SKTextureAtlas atlasNamed:atlasName];
NSInteger numberOfTiles = tileAtlas.textureNames.count;
backgroundTiles = [[NSMutableArray alloc] initWithCapacity:numberOfTiles];
for (int y = 0; y < 5; y++) {
for (int x = 0; x < 9; x++) {
int tileNumber = y*9 + x + 1;
NSString *textureName = [NSString stringWithFormat:#"tile%d.png",tileNumber];
SKSpriteNode *tileNode = [SKSpriteNode spriteNodeWithTexture:[tileAtlas textureNamed:textureName]];
CGPoint position = CGPointMake((0.5 + x)*_tileSize - _levelWidth/2,(0.5 - y - 1)*_tileSize + _levelHeight/2);
tileNode.position = position;
tileNode.zPosition = -1.0f;
tileNode.blendMode = SKBlendModeReplace;
[(NSMutableArray *)backgroundTiles addObject:tileNode];
}
}
Then i use this code to add them to my scene:
- (void)addBackgroundTiles
{
for (SKNode *tileNode in [self backgroundTiles]) {
[self addChild: tileNode];
}
}
The problem is it doesnt load the correct texture for a tile or find the texture at all.
What I end up with is this (ignore the blue circle): http://i.stack.imgur.com/g39BF.png
Here is my tile atlas: http://snk.to/f-ctp5yhpz
EDIT: I am using NameChanger(www.mrrsoftware.com/MRRSoftware/NameChanger.html) to rename all my tiles, can it be that program that messes up my pngs? as far as i can see they are in the correct order after i have renamed them.
Solution
Editing my answer to point out that the solution is in the comments below this answer.
It turned out that the issue was caused by Xcode not rebuilding the atlas after the image files were renamed outside of Xcode (presumably by the file changed OP mentioned).
By cleaning and rebuilding the project, all the texture atlases were built again, and OPs code started working.
Original answer
Two things to double-check:
Is your .atlas added to your project as a folder or a group? It must be a folder (blue icon in Xcode, instead of yellow).
After adding Tiles.atlas to your project, you must also enable atlas generation in Xcode settings.
See here for a similar issue: How to create atlas for SpriteKit. I linked to Apple documentation on incorporating texture atlases into your projects which has a detailed step-by-step instruction on enabling atlas generation.
Why the double for loops?
Are you saving the backgroundTiles array as a property?
I've had this occur recently and the only fix that worked was:
[SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:#"someTile.png"]]; The textureWithImageNamed always gets the right one.
So try:
SKSpriteNode *tileNode = [SKSpriteNode spriteNodeWithTexture:[SKTexture textureWithImageNamed:textureName]];

Sprite Kit - Animations & memory managment

I'm currently messing around with Sprite Kit on iOS to figure out if it would be a fitting framework to make relatively simple 2D game in.
Due to my ActionScript background, i am very comfortable working with Sprite Kit code-wise
But there is something i just can't figure out. Animated nodes with Texture Atlas as a resource are incredibly memory heavy. I've imported an atlas into my project (size of textures is about 35MB). Preloading textures into RAM seems ok but at the moment i run the actual animation, the heap size increases exponentinaly (from about 80MB to 780MB)
Here goes my code:
self.noahFrames = [[NSMutableArray alloc] init];
SKTextureAtlas *noahAtlas = [SKTextureAtlas atlasNamed:#"noahAnimati"];
int imgCount = noahAtlas.textureNames.count;
for (int i=1; i <= imgCount; i++) {
NSString *textureName = [NSString stringWithFormat:#"NoahMainMenuAnimation_%d", i];
SKTexture *temp = [noahAtlas textureNamed:textureName];
[self.noahFrames addObject:temp];
}
SKSpriteNode *noahNode = [self createSpriteWithName:#"noah" imagePath:#"Noah_main_menu_hd" positionXPath:#"MainMenu.Noah.x" positionYPath:#"MainMenu.Noah.y" scalePath:#"MainMenu.Noah.scale"];
[self addChild:noahNode];
//up to this point everything goes fine
[noahNode runAction:[SKAction repeatActionForever:
[SKAction animateWithTextures:self.noahFrames
timePerFrame:0.1f
resize:YES
restore:YES]] withKey:#"animatedNoah"];
So i guess my actual question is why does the application become that insanely memory heavy after calling the SKAction animation ? I must be missing something rather obvious ...
I do know that when a texture is loaded in graphic memory it's loaded without any compression, but I don't think that xcode monitors graphic memory, so it's really strange to me.
I usually load and execute animations just like you do and I don't have such memory behaviour, but i noticed it when testing on the simulator. Are you using iOS simulator for your tests? Does your application crash when you reach those memory levels?

Keeping track of objects on the screen

Even though I’m writing in Objective C, most of my code is still written in a procedural style. However, now I want to do something where that approach will not work. So I need some advice on how to deal with an indeterminate number of objects on the screen at the same time. I’m sure that this problem has been solved, I just haven’t been able to find out how.
I have a bunch of games where I put two or four pictures on the screen and then the user interacts with the picture. When they are done with a page they swipe to the next one and I use a transition to slide the pictures off the screen. I can control the movement of the pictures because when they were created I name them self.picture_1 and self.picture_2. The movement method knows about them even though that method didn’t create them.
Now suppose I want to have an indeterminate number of pictures on the screen. I can’t call them self.picture_1. through self.picture_n because ObjectiveC won’t let you dynamically create variable names. But I still need to move them in a method where they weren’t created.
I can make it work with two techniques, neither of which seem ideal. First, I look at all the objects on the screen and then do something with the ones that I want to target. Note: pictures are in buttons.
for ( id subview in self.parentView.subviews ) {
if ( [subview isKindOfClass:[UIButton class]] ) {
UIButton *pictureButton = subview;
for (NSUInteger i=0; i<self.totalItems; i++) {
NSUInteger row = (i % 2) + 1;
NSUInteger column = (i/2) + 1;
NSString *pictureTitle = [NSString stringWithFormat:#"pictureR%iC%i", row, column];
if ( [pictureButton.titleLabel.text isEqualToString:pictureTitle] ) [pictureButton removeFromSuperview];
}
}
}
This works for removing them from the view, but gets cumbersome when I try to make the pictures slide off the screen.
The second way is to make an array that holds the picture objects when they are created. I’ve been playing with something like this.
self.gridImages = [NSMutableArray arrayWithCapacity:4];
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Word *word = [wordListArray objectAtIndex:i];
self.gridImages[i] = word.image;
}
And then to do things with the pictures I loop through the array.
for (NSUInteger i = 0; i < itemsOnScreen; i++) {
Picture *picture = self.gridImages[i];
// do something with picture
}
Neither of these methods seems ‘right’ so I’m wondering if there is a preferred method for manipulating an indeterminate number of objects on the screen?
#Hot Licks, that works, so I put you answer into an answer.
I have it working for checkBoxes. They are created by the main view controller and it passes in a tag. I'm using tags starting at 1000 for checkboxes, 2000 for pictures, etc. Just before I put the checkBox on the screen, I assign it the tag.
[self.checkBox setTag:checkBoxTag];
You could also use: self.checkbox.tag = checkBoxTag;
When it is time to remove the checkBoxes, I loop through all of the tags starting at 1000 up to the total number of items on the screen. I have warnings turned up to 11 so I need to cast the counter to an NSInteger.
- (void)removeCheckboxes {
for (NSUInteger i = 0; i < self.totalItems; i++) {
NSInteger tagNumber = 1000 + (NSInteger)i;
[ [self.parentView viewWithTag:tagNumber] removeFromSuperview];
}
}

Apparent leaks: png_malloc

I have an application with various animations and images. The application runs just fine for about 30 minutes, but then crashes. I have looked through the instruments and I notice that there are a whole bunch of 7kB png_malloc allocations building each time I mark the heap (amounting to about 300kB every couple minutes).
I noticed in my leaks that every time an animation or png is used for the first time, there seems to be a "leak" of the data (although I am a bit skeptical whether this is a real leak or not).
All of these images have been declared using
frameName = [[NSString alloc] initWithFormat:#"image.png"];
UIImage * u = [UIImage cachelessImageNamed:frameName];
so I don't believe there should be a problem with caching the images.
Has anyone else had the same problem with this png_malloc allocation?
The instruments screenshot
*Notes: I am using arc and the animations are getting set to nil in the deallocation function; however, these isn't called until the application exits. Does this create a problem each time the animation is run if it's only been created once?
EDIT Some more code:
-(void) createSymbolAnimations
{
if (symbolAnimations == nil)
{
symbolAnimations = [[NSMutableArray alloc]init];
}
NSString * frameName;
if (thisAnimation == nil)
{
thisAnimation = [[NSMutableArray alloc] init];
}
for (int x= 0; x< 40; x++)
{
frameName = [[NSString alloc] initWithFormat:#"image%d%s",x,".png"];
UIImage * u = [UIImage cachelessImageNamed:frameName];
[thisAnimation addObject:u];
}
[symbolAnimations addObject:thisAnimation];
}
Is the creation of the animation. Imagine I have a few of these and then I change the animation set and start animating on touch with this snippet:
UIImageView * aView = [frameArray objectAtIndex:x];
aView.image = [[symbolAnimations objectAtIndex:x]objectAtIndex:0];
[aView startAnimating];
Where x is the set of images I want to animate and 0 is the first frame of the animation.
So the image is changed quite a few times and I'm starting to worry that each time the animation images are changed, the RAM isn't cleared but instead over/rewritten.
EDIT Image grabber
+(UIImage *) cachelessImageNamed: (NSString *) name
{
return [UIImage imageWithContentsOfFile:[[NSBundle mainBundle] pathForResource:name ofType:nil]];
}
Just in case anyone stumbles upon this later, I found the problem.
The pngs used in this project for animations were created in Windows (not sure how pertinent that is) and it seems the file format is slightly different than the png that XCode is expecting. This disallows any png from being deallocated. If you convert the format to a png for Mac, it seems to work fine. I did this through
mogrify -type truecolormatte -format png *.png
After adjusting all of my images, the leaks were greatly reduced and everything seems to run fine.

Resources