1

I am having trouble caching a large texture atlas (almost full 2048x2048) and using it for animatewithtextures. It lags initially on the first load. After that it is ok. With all of my other atlases I just put them in a singleton and save them off using the method from the following link: https://www.codeandweb.com/blog/2013/09/23/spritekit-animations-and-textureatlases self.atlas = [SKTextureAtlas atlasNamed:SPRITES_ATLAS_NAME]

It seems as though it is loading and then unloading the texture atlas in memory. Has anyone else experienced this? Using Swift 1.2, SpriteKit, Texture Packer

runcmd3
  • 71
  • 4
  • You should be more accurate. How big are textures, how many of them do you store in the atlas ? How fast is your animation (timePerFrame)? Have you tried to isolate code where you animate with textures to be sure that there is no something else which makes initial lag (sounds can make lag at initial load). http://stackoverflow.com/questions/28041605/sprite-kit-animatewithtextures-lags – Whirlwind Apr 07 '15 at 20:05
  • If you don't keep a reference to it, SK might decide to dump it from memory. – sangony Apr 07 '15 at 22:00
  • Thank you for your replies. The size of each texture is 640x640 and there are 39 individual textures. They were designed to fit into one 2048x2048 sheet and there is a lot of blank space around the edges. Even when I don't call the animation and just try to use one of the textures it still does the initial lag. I am keeping a reference to it inside a singleton the swift 1.2 way. class GameTextures { static let sharedInstance = GameTextures(); var explosionAtlas:SKTextureAtlas; } init() {self.explosionAtlas = SKTextureAtlas(named: "Explosion")} – runcmd3 Apr 08 '15 at 00:16
  • Do not use singletons, this design pattern is a memory leak by definition. Hang a ref to a SKTexture off the scene and implement loading/unloading in terms of the scene. Let SpriteKit manage the currently loaded atlases and focus on reducing the memory use of the actual textures in your app. The load time you are seeing is when the original PNG is decoded and then turned into an uncompressed texture, you are not going to be able to work around that. – MoDJ Jul 31 '16 at 20:09
  • Just a further FYI, you might want to take a look at the examples mentioned here, included is a very large SpriteKit Fire animation that uses a 1000x1000 animation at 32BPP: https://stackoverflow.com/questions/22470907/sktextureatlas-loading-and-memory-management/38679128#38679128 – MoDJ Aug 26 '16 at 22:43

3 Answers3

1

By using the Singleton pattern in your GameTextures class you are probably just keeping away self.explosionAtlas from being initialized twice. But I think you are not preloading textures actually. You have to preload them first and then keep them from being released by holding strong reference to them like @sangony pointed. Storing reference like self.explosionAtlas does not automatically preload textures from that atlas into memory. You can check this in debug navigator by looking at memory consumption when you preload textures manually into array or using method like + preloadTextureAtlases:withCompletionHandler: vs doing something like self.atlas = [SKTextureAtlas atlasNamed...]

One thing I would do is to remove code related to that animation completely to see if app lags at start when the scene is presented. Just to be sure where the problem is.

Also check your timePerFrame parameter... Don't set it too low... If you are using 39 images just for one explosion I think you should try with little less frames and setting timePerFrame parameter to higher value. I have an explosion with 10 images with timePerFrame set to 0.06 and still looks great. You are certainly aware that devices have limited memory...iPhone 4s has 512 megabytes of RAM memory but it will crash when memory consumption is about 200 - 250 MB. So be careful with big animations/textures because textures consume most of app's memory . Check this out

To calculate how much image will take into memory you can read here.

Note that you can make great explosions with SKEmitterNode if you decide to give up on current animations, but I still think that you are not caching textures properly and they are not ready before animation is called (as well as you are using small timePerFrame interval, like 0.033 or something like that).

Hope this will lead you somewhere.

Community
  • 1
  • 1
Whirlwind
  • 14,286
  • 11
  • 68
  • 157
1

I did end up finding a solution to this. All you have to do is add an item to the scene before runtime to make sure it decompresses the atlas. I wrote a function to do this to all my atlases in the atlas/texture controller. Apple said there is no built in function to do this, and the preload function will not perform the decompression.

runcmd3
  • 71
  • 4
0

If you are using a singleton to store all your animations, I suggest organizing it with these examples in mind.

Singleton Header File

-(void)loadPlayer0Atlas;
@property (strong) SKTextureAtlas *player0Atlas;
@property (strong) SKAction *player0_walk;
@property (strong) SKAction *player0_jump;
@property (strong) SKAction *player0_shoot;

Singleton Implementation File

-(void)loadPlayer0Atlas {
    if(self.player0Atlas == nil) {
        NSLog(@"loading player0AtlasLoaded");
        self.player0Atlas = [SKTextureAtlas atlasNamed:PLAYER0ATLAS_ATLAS_NAME];
        [SKTextureAtlas preloadTextureAtlases:[NSArray arrayWithObject:self.player0Atlas] withCompletionHandler:^{
            [self loadPlayer0Assets];
        }];
    } else {
        NSLog(@"no load needed for player0AtlasLoaded");
        [[NSNotificationCenter defaultCenter]postNotificationName:@"player0AtlasLoaded" object:self];
    }
}

-(void)loadPlayer0Assets {
    self.player0_walk = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_WALK timePerFrame:0.1]];
    self.player0_jump = [SKAction animateWithTextures:PLAYER0ATLAS_ANIM_JUMP timePerFrame:0.05];
    self.player0_shoot = [SKAction repeatActionForever:[SKAction animateWithTextures:PLAYER0ATLAS_ANIM_SHOOT timePerFrame:0.1]];
    [[NSNotificationCenter defaultCenter]postNotificationName:@"player0AtlasLoaded" object:self];
}

The strong class properties keep a reference to your atlas and animations. You can call the loadPlayer0Atlas method to load the atlas and animations. Once loaded, a NSNotification message is delivered letting you know in your (for example) GameScene.

You would call the animations like this:

[playerNode runAction:[singleton player0_walk] withKey:@"animation"];
sangony
  • 11,636
  • 4
  • 39
  • 55