Preloading textures in SpriteKit - ios

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.

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.

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.
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)

Related

Use multiple asset folders in xCode

So I am working on a game in SpriteKit. I have a function that generates a level for the player.
The function uses images placed in assets.xcassets to generate levels. The game has multiple themes , namely fire water and electricity. Each theme has its own set of images with the same names but each theme image set is in its separate folder/group. Is it possible for me to programmatically tell the function to use a specific folder so that I can generate levels for other themes without renaming images. Or is it possible for me to use multiple asset folders and change which one the function uses at runtime. I want to use a random theme every time the player restarts the level.
Sprite Atlas / Sprite Sheet
You can use a Sprite Atlas (also known as Sprite Sheet).
If you don't know what I'm talking about have a look at these 2 nice videos:
Sprite Sheets - The Movie Part 1
Sprite Sheets - The Movie Pt. 2 - Performance
How to use it
If you have watched the videos now you know that the main target of a Sprite Atlas is loading into the GPU all and only (as far as possible) the images needed for the current scene or level. This way drawing each frame becomes much faster because all the resources al already on the GPU and the CPU only needs to transmit the coordinates.
Namespace
Ok but how does it solve your problem?
Xcode allows you to create a namespace for each Sprite Atlas. So you can use the same name for resources in different Sprite Atlas.
How to create a Sprite Atlas.
This is pretty simple.
With Xcode open you Asset Catalog
Tap on the + button you see in the image below
Click on New Sprite Atlas
You'll se a new "Folder", that's your Sprite Atlas.
5. Now click on the Provides Namespacecheckbox on the right.
That's it. Now just drop your images into the Sprites folder in order to add them to your sprite atlas.
In this example I'm going to rename the Sprite Atlas as Sprites0 and will add a red circle named circle.
Then
will create another Sprite Atlas
will check again the Provides Namespacecheckbox
will name Sprites1
and finally will add a green circle names circle.
Here's the final result
Code
Let's see now how to use it.
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let textureAtlas0 = SKTextureAtlas(named: "Sprites0")
let texture0 = textureAtlas0.textureNamed("circle")
let sprite0 = SKSpriteNode(texture: texture0)
sprite0.position.y = 100
addChild(sprite0)
let textureAtlas1 = SKTextureAtlas(named: "Sprites1")
let texture1 = textureAtlas1.textureNamed("circle")
let sprite1 = SKSpriteNode(texture: texture1)
sprite1.position.y = -100
addChild(sprite1)
}
}
Result
How does it work
Step 1: you load the Sprite Atlas
let textureAtlas0 = SKTextureAtlas(named: "Sprites0")
Step 2: you load a specific texture from the texture atlas
let texture0 = textureAtlas0.textureNamed("circle")
Step 3: you use your texture as you want
let sprite0 = SKSpriteNode(texture: texture0)
sprite0.position.y = 100
addChild(sprite0)

iOS: Can I use Texture Atlas already created with Adobe Animate?

I know that I can add sequences of individual images in Xcode, and let it create the Texture Atlas. This process is well-described in easily-searched venues. (Like this one.)
I am getting atlases from designers, created with Adobe Animate (v18.0) already combined into the full sheet, and moreover with the accompanying XML file describing the animation. (In which sub-images and display frames do not match 1:1, so it's hard to see how Xcode would figure that out.)
It's not clear to me from the SpriteKit docs whether/how to use these pre-defined Texture Atlases. Is this possible?
If you're getting pre-baked texture atlases, with externally-generated descriptions of where the sprites should get their textures, you'll probably have to create your sprites using SKTextures that you create using the init(rect:in:) initializer of a SKTexture.
You'll need to read the sprite's extents out of the XML file, and then create a texture out of the atlas. Then you can create a new SKTexture object that represents a part of the larger texture to act as your sprite's texture.
This is untested pseudocode, but it shows the process:
let spriteRect = (get the rect from the XML)
let atlas = SKTexture( imageNamed: "myTextureAtlas" )
let spriteTexture = SKTexture( rect:spriteRect, in:atlas )
let sprite = SKSpriteNode( texture:spriteTexture )
Once you have this process in place, you can animate the sprites using the usual methods, like setting up SKActions with a list of textures out of the texture atlas.

Use image as tileset in Spritekit

I don't find a way to use this sort of images as tile set for my games.
Is there a way to do it, or should I add all object I want one by one ?
I tried to add a new Grid Tile Set then a Single Tile Group, but if I try to use this in a Tile Map Node, it will use all the image as texture for one block.
I don't know if it's possible, but if you can help me, I would be grateful.
Unfortunately you will have to separate the image into smaller images manually..
The functionality you want just doesn't exist.
You can use your image as a texture atlas, but you will not be able to use SKTextureAtlas, you will have to break them out your self.
If you look at SKTexture, you have an init called init(rect: CGRect, in: SKTexture). This will create a new SKTexture object for you, while still referencing the texture memory space of the original texture. You are just going to have to use something like a plist to load in all of the CGRRect info to create this atlas.
Example:
let textureAtlas = SKTexture(imageNamed:"CEm72.png") //I convert your jpg to png somehow
let rectDarkKnight = CGRect(x:0,y:96,width:32,height:32)
let texDarkKnight = SKTexture.init(rect: rectDarkKnight, in: textureAtlas)

How to release memory created by SKTexture

I have an application that creates scene kit textures using the following:
let texture = SKTexture(image: image)
The problem is that after several calls, the application crashes due to low memory.
I really only need the most recently created texture so when creating a new one, the preview ones' memory can be released, however, Scene Kit seems to be holding on to these textures even when they aren't being used anymore (the nodes are no longer part of the scene).
How can I manually tell Scene Kit to release the memory and cache of the texture so I don't get into the low memory crashing situation?
Also, I know that the SceneKit documentation recommends removing all references to the texture by removing nodes and such, but that should be happening and yet the Texture is not being released. Is there a good way of debugging and seeing what's holding on to the reference?
In re-reviewing the code, I'm creating a scene to hold the image to be rendered in the primary scene (whose node doesn't change). Basically, there is an object in the scene that can display an image or a video and that gets changed out by returning a Scene which is used as the material diffuse contents.
let texture = SKTexture(image: image)
let sprite = SKSpriteNode(texture: texture)
let scene = SKScene(size: sprite.size)
sprite.position = CGPointMake(CGRectGetMidX(scene.frame), CGRectGetMidY(scene.frame));
scene.addChild(sprite)
And then being used here:
primaryObjectMaterial.diffuse.contents = scene
I would expect once the scene's node material contents are changed, this scene (and the texture and image) would be released as there would no longer be any references to it but perhaps the caching system is hanging onto something or I'm doing something wrong?
It's hard to give you an exact answer with out seeing your code or knowing how the app works but you need to take the node that the texture is on and remove it
nameOfNode.removeFromParentNode
This will remove it from memory and you need to add a new node with the new texture.

SpriteKit SKTexture is not passed as reference to SKSpriteNode

Apple recommends to use one SKTexture object if it will be used multiple times (https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKTexture_Ref/).
So I wanted to test if it is really passed as reference:
var fireSpriteTexture:SKTexture = SKTexture(imageNamed: "fire")
let fireSprite = SKSpriteNode(texture: fireSpriteTexture)
fireSprite.position = CGPointMake(50,50)
fireSprite.zPosition = 100
// Change texture -> We should see no fire
fireSpriteTexture = SKTexture(imageNamed: "water")
self.addChild(fireSprite)
In my understanding this swift code should make the water texture visible in my app BUT in fact the fire texture is placed on the screen.
Why? Is it a bug?
To make it more clear I would also like to know what happens if I init my node with SKSpriteNode(imageNamed: "fire") and then copy it with its copy() method, do both objects share the same "fire" resource?
I do not think you are understanding pass by reference correctly, you pass the reference of fire into your sprite, the sprite will then retain a reference of this texture.
You are changing the fire texture to a new texture with a new reference. This does not mean the sprite with the old reference is going to automatically change to the new reference.
If you want to check for reference, you want to compare the fire texture to the sprite texture, not create a whole new texture.
If you want to change the texture, then you need to edit the data of the texture without creating a new reference.

Resources