2

When you preload textures using the spritekit preloadTextures function, it loads all the textures in the provided array into the memory at once.

If you don't have the ability to split up your levels in your game with 'loading screens' but do have separate levels with different image files than each other, how can you keep from storing all the images in memory at once without sacrificing frame rate when spritekit loads the images when it needs to?

Undo
  • 25,519
  • 37
  • 106
  • 129
Max Hudson
  • 9,961
  • 14
  • 57
  • 107
  • Only load the textures you need for the current level? Not sure what is is you want to know or trying to do. – CodeSmile Feb 19 '14 at 09:42

1 Answers1

1

You could create a singleton class with methods for loading and unloading resources specific to the level you are currently playing. For instance, if you have textures a, b, and c that need to be loaded for level one, and textures x, y, and z for level 2, you could have a method -(void)loadLevelOneTextures; and a -(void)loadLevelTwoTextures; as well as a -(void)unloadLevelOneTextures; and -(void)unloadLevelTwoTextures;

This way, you can tell the singleton to load the textures before you need them, and when you're done you tell it to release them.

//GameTextureLoader.h
//
@import SpriteKit;
@import Foundation;
@interface GameTextureLoader : NSObject
@property (strong, nonatomic)NSMutableArray *levelOneTextures;
@property (strong, nonatomic)NSMutableArray *levelTwoTextures;
+ (GameTextureLoader *)sharedTextures;
- (void)loadLevelOneTextures;
- (void)unloadLevelOneTextures;
- (void)loadLevelTwoTextures;
- (void)unloadLevelTwoTextures;

And the implementation:

//GameTextureLoader.m
//
#import "GameTextureLoader.h"
@implementation GameTextureLoader
+ (GameTextureLoader *)sharedTextures{
    static dispatch_once_t onceToken;
    static id sharedInstance;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}
- (instancetype)init{
    self = [super init];
    if (self)
    {
            self.levelOneTextures = [[NSMutableArray alloc] init];
            self.levelTwoTextures = [[NSMutableArray alloc] init];
        return self;
    }
    else{
        exit(1);
    }
}
- (void)loadLevelOneTextures{
        //Order of images will determin order of textures
    NSArray *levelOneImageNames = @[@"imageA", @"imageB", @"imageC"];
    for (NSString *image in levelOneImageNames){
        SKTexture *texture = [SKTexture textureWithImageNamed:image];
        [self.levelOneTextures addObject:texture];
    }
}
- (void)loadLevelTwoTextures{
        //Order of images will determin order of textures
    NSArray *levelTwoImageNames = @[@"imageX", @"imageY", @"imageZ"];
    for (NSString *image in levelTwoImageNames){
        SKTexture *texture = [SKTexture textureWithImageNamed:image];
        [self.levelTwoTextures addObject:texture];
    }
}
- (void)unloadLevelOneTextures{
    [self.levelOneTextures removeAllObjects];
}
- (void)unloadLevelTwoTextures{
    [self.levelTwoTextures removeAllObjects];
}

You would do this for each level you have, and then to access the the textures you would do something like this. (Be sure to first import GameTextureLoader.h)

GameTextureLoader *loader = [GameTextureLoader sharedTextures];
[loader loadLevelOneTextures];
SKSpriteNode *node = [SKSpriteNode spriteNodeWithTexture:loader.levelOneTextures[0]];
[self addChild:node];
sfdrew
  • 155
  • 2
  • 9
Andrew97p
  • 565
  • 6
  • 11
  • To make this even better for performance, use an asynchronous dispatch queue when you call load textures. If you have questions on how to do this, look up how to dispatch a task asynchronously with GCD – Andrew97p Feb 19 '14 at 22:00
  • 1
    Excuse me. Don't you have to initialize those mutablearrays in GameTextureLoader before storing SKTexture? – El Tomato Mar 04 '14 at 07:31
  • @ElTomato Yes, I'm sorry, i'll fix that. – Andrew97p Mar 04 '14 at 11:10
  • So I should call this a couple seconds or so before I use these textures correct? And this won't produce lag? – Max Hudson Mar 11 '14 at 05:24
  • 1
    @maxhud as this code is written now, when you call preload it ribs on the main thread, so as it is now you could call it the line before you needed it an be fine, but if you do use it as it is, it will likely produce lag if you have lots of textures or they are large. Like I said in the first comment, if you use GCD to dispatch asynchronously, and made sure that your dictionary was non nil before accessing it and did call preload well before needed, then it should produce no lag because it won't load on the main thread. – Andrew97p Mar 11 '14 at 10:10
  • Okay and how do I make sure my dictionary non nil before accessing it? not exactly sure what this is referencing as I don't see a dictionary object? – Max Hudson Mar 11 '14 at 17:50
  • @maxhud you check it against nil 'if(dictionary){code here}' – Andrew97p Mar 11 '14 at 21:45
  • @Andrew97p I am using your solution to load textures. Its great by the way but I have big textures loading and a lot of them. So I am trying to use dispatch queue but still the first time any of my textures load, the game lags a bit. I am using dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0). Can you help? – Swati Rawat Sep 24 '14 at 08:17
  • @SwatiRawat You should use dispatch_async – Andrew97p Sep 25 '14 at 11:19
  • @Andrew97p I am sorry for typing mistake. I am already using dispatch_async not dispatch_sync – Swati Rawat Sep 26 '14 at 06:25
  • Just an FYI here, but this singleton based approach is very bad advice. It is far far too easy to leave singleton references handing off global memory. Any texture you are using can be a strong ref directly from the GameScene, when you game scene goes away the ref is dropped. SpriteKit already does caching for you behind the scenes, you are just asking to shoot yourself in the foot with this singleton stuff. A singleton is just a memory leak in a ref counted environment. – MoDJ Aug 04 '16 at 22:29