Preloading textures in SpriteKit
Asked Answered
P

2

10

I've done some research and I can't seem to find anything that clearly explains how to go about preloading both single textures and textures within animations. I'm currently using Atlas's in Assets.xcassets to group related animation images. Does having my images in the Atlas mean that they are preloaded? As far as single images, does it make sense to declare the texture before GameScene like this: let laserImage = SKTexture(imageNamed: "Sprites/laser.jpg") and then (for example) within one of my SKSpriteNode subclass I can just pass laserImage through?

I ultimately wanted to know if there was a well defined way of going about this or if I should just store each texture as a constant before GameScene. Any advice on the proper (and most efficient) way of going about this would be great.

Pandurate answered 7/8, 2016 at 20:24 Comment(0)
B
8

One Single Texture Atlas

Put all your assets into a single Sprite Atlas. If they don't fit, try at least to put all the assets of a single scene into a single Sprite Atlas

Preloading

If you want you can preload the texture atlas in memory with this code

SKTextureAtlas(named: "YourTextureAtlasName").preloadWithCompletionHandler { 
    // Now everything you put into the texture atlas has been loaded in memory
}

Automatic caching

You don't need to save a reference to the texture atlas, SpriteKit has an internal caching system. Let it do it's job.

Which name should I use to reference my image?

Forget the name of the file image, the name you assign to the image into Asset Catalog is the only name you will need.

enter image description here

How can I create a sprite from an image into a texture atlas?

let texture = SKTextureAtlas(named:"croc").textureNamed("croc_walk01")
let croc = SKSpriteNode(texture: texture)
Buchner answered 7/8, 2016 at 22:22 Comment(10)
After I call .preloadWithCompletionHandler on the Atlas, how should I reference the images? Would I just reference the image name with no extension? Also, does this mean that it never makes sense to have separate Atlas's for groups of related images that pertain to one animation? Thanks a lot.Pandurate
Thanks so much, and also is there any performance benefit from storing ALL my textures in a single Atlas? Or should I separate them according to ones that belong to a single animation and another that holds individual textures that aren't necessarily part of an animation?Pandurate
Absolutely you should use one single Texture Atlas because it is loaded into the GPU and, as long as you only use texture from that sprite atlas, SpriteKit won't need to load any other resource to the GPU.Buchner
iirc when preloading the atlas, with preloadWithCompletionHandler, the code from the completion block is executed on the background thread which might lead to problems if your code needs to be executed on the main thread (so you need to grab the main queue in that completion handler and execute it there).Activist
I have found the relevant post.You can read more here : https://mcmap.net/q/1162154/-delay-when-unhiding-uibuttons-after-presenting-skscene About the "one atlas", I agree with @appzYourLife , but I would say, that you have to find what is optimal for your app. If you have one big atlas, with all images, but you use only few images from it in a current scene, then memory will be wasted in that scene. In that case two atlases ( which will require two draw calls) would be more optimal solution.Activist
@appzYourLife Yup. IMO, it is unexpected behavior, but the code from the completion handler is executed on the backgoround thread.Activist
Could you provide a bit of usage code for the .preloadWithComplet‌​ionHandler?Pandurate
@appzYourLife Are you sure about "You don't need to save a reference to the texture atlas, SpriteKit has an internal caching system."? Is there a documentation about this?Roarke
@appzYourLife I'm asking because I've tried this and if I don't store/retain a reference to the array of texture atlases that I get in the completion block of SKTextureAtlas.preloadTextureAtlasesNamed, the performance is really bad; ie. the textures are clearly not in the memory. If I keep the reference to that array of texture atlases everything seems to work smoothly.Roarke
@appzYourLife Also, look closely in developer.apple.com/library/content/documentation/… at this sentence: "You need to add code to provide these texture objects to the scene". If I understand it correctly, it says that you have to store a reference to the preloading result you get in the completion block. Which makes sense, because how would otherwise SpriteKit know when it's a good time to release the preloaded textures from its internal cache, if it would have one like you described?Roarke
O
13

I tried to implement appzYourLife answer but it increased the time to load every texture. So I ended up putting all the most used Sprites in one single atlas and puting it in a singleton.

class Assets {
    static let sharedInstance = Assets()
    let sprites = SKTextureAtlas(named: "Sprites")

    func preloadAssets() {
        sprites.preloadWithCompletionHandler {
            print("Sprites preloaded")
        }
    }
}

I call Assets.sharedInstance.preloadAssets() in menuScene. And:

let bg1Texture = Assets.sharedInstance.sprites.textureNamed("background1")
bg1 = SKSpriteNode(texture: bg1Texture)

to reference a texture already loaded in memory.

Obryant answered 12/9, 2016 at 1:21 Comment(4)
This works very well-- exactly what I've been looking for. Is there a convention to use for displaying a load screen while the sprites are preloaded?Pandurate
@Obryant I'm seeing the same results. I don't think SpriteKit is internally caching preloaded textures or atlases. This makes sense though, because how would the SpriteKit know when is a good time to clear the cache, right? I'm seeing a big performance increase if I keep the reference to the preloaded textures/atlases.Roarke
I believe this should be the correct solution. The example that apple provides shows that they also keep a reference to the textures. They reference an array of textures and call the preload method on that variable or property.Setscrew
Thank you. As for me your solution should be accepted as correct answer. upvotedGobioid
B
8

One Single Texture Atlas

Put all your assets into a single Sprite Atlas. If they don't fit, try at least to put all the assets of a single scene into a single Sprite Atlas

Preloading

If you want you can preload the texture atlas in memory with this code

SKTextureAtlas(named: "YourTextureAtlasName").preloadWithCompletionHandler { 
    // Now everything you put into the texture atlas has been loaded in memory
}

Automatic caching

You don't need to save a reference to the texture atlas, SpriteKit has an internal caching system. Let it do it's job.

Which name should I use to reference my image?

Forget the name of the file image, the name you assign to the image into Asset Catalog is the only name you will need.

enter image description here

How can I create a sprite from an image into a texture atlas?

let texture = SKTextureAtlas(named:"croc").textureNamed("croc_walk01")
let croc = SKSpriteNode(texture: texture)
Buchner answered 7/8, 2016 at 22:22 Comment(10)
After I call .preloadWithCompletionHandler on the Atlas, how should I reference the images? Would I just reference the image name with no extension? Also, does this mean that it never makes sense to have separate Atlas's for groups of related images that pertain to one animation? Thanks a lot.Pandurate
Thanks so much, and also is there any performance benefit from storing ALL my textures in a single Atlas? Or should I separate them according to ones that belong to a single animation and another that holds individual textures that aren't necessarily part of an animation?Pandurate
Absolutely you should use one single Texture Atlas because it is loaded into the GPU and, as long as you only use texture from that sprite atlas, SpriteKit won't need to load any other resource to the GPU.Buchner
iirc when preloading the atlas, with preloadWithCompletionHandler, the code from the completion block is executed on the background thread which might lead to problems if your code needs to be executed on the main thread (so you need to grab the main queue in that completion handler and execute it there).Activist
I have found the relevant post.You can read more here : https://mcmap.net/q/1162154/-delay-when-unhiding-uibuttons-after-presenting-skscene About the "one atlas", I agree with @appzYourLife , but I would say, that you have to find what is optimal for your app. If you have one big atlas, with all images, but you use only few images from it in a current scene, then memory will be wasted in that scene. In that case two atlases ( which will require two draw calls) would be more optimal solution.Activist
@appzYourLife Yup. IMO, it is unexpected behavior, but the code from the completion handler is executed on the backgoround thread.Activist
Could you provide a bit of usage code for the .preloadWithComplet‌​ionHandler?Pandurate
@appzYourLife Are you sure about "You don't need to save a reference to the texture atlas, SpriteKit has an internal caching system."? Is there a documentation about this?Roarke
@appzYourLife I'm asking because I've tried this and if I don't store/retain a reference to the array of texture atlases that I get in the completion block of SKTextureAtlas.preloadTextureAtlasesNamed, the performance is really bad; ie. the textures are clearly not in the memory. If I keep the reference to that array of texture atlases everything seems to work smoothly.Roarke
@appzYourLife Also, look closely in developer.apple.com/library/content/documentation/… at this sentence: "You need to add code to provide these texture objects to the scene". If I understand it correctly, it says that you have to store a reference to the preloading result you get in the completion block. Which makes sense, because how would otherwise SpriteKit know when it's a good time to release the preloaded textures from its internal cache, if it would have one like you described?Roarke

© 2022 - 2024 — McMap. All rights reserved.